EBernhardson has uploaded a new change for review.

  https://gerrit.wikimedia.org/r/311654

Change subject: Integrate did you mean collection into search satisfaction
......................................................................

Integrate did you mean collection into search satisfaction

The did you mean information is much more useful directly paired with
the search satisfaction schema. As such merge it together, and add some
test cases to validate data is collected as epected

Bug: T138087
Change-Id: I8983a9677ffd9d98c8a347ef0961c2f0e874393f
---
M WikimediaEventsHooks.php
M extension.json
D modules/ext.wikimediaEvents.didyoumean.js
M modules/ext.wikimediaEvents.searchSatisfaction.js
M tests/browser/SearchSatisfactionTests.php
5 files changed, 162 insertions(+), 151 deletions(-)


  git pull 
ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/WikimediaEvents 
refs/changes/54/311654/1

diff --git a/WikimediaEventsHooks.php b/WikimediaEventsHooks.php
index 50bfe0c..995b2cb 100644
--- a/WikimediaEventsHooks.php
+++ b/WikimediaEventsHooks.php
@@ -458,7 +458,6 @@
                $wgOut->addJsConfigVars( array(
                        'wgIsSearchResultPage' => true,
                ) );
-               $wgOut->addModules( 'ext.wikimediaEvents.didyoumean' );
 
                return true;
        }
diff --git a/extension.json b/extension.json
index 24558a9..be79cbe 100644
--- a/extension.json
+++ b/extension.json
@@ -101,11 +101,6 @@
                        "schema": "Edit",
                        "revision": 13457736
                },
-               "schema.DidYouMean": {
-                       "class": "ResourceLoaderSchemaModule",
-                       "schema": "DidYouMean",
-                       "revision": 13800499
-               },
                "schema.CompletionSuggestions": {
                        "class": "ResourceLoaderSchemaModule",
                        "schema": "CompletionSuggestions",
@@ -114,7 +109,7 @@
                "schema.TestSearchSatisfaction2": {
                        "class": "ResourceLoaderSchemaModule",
                        "schema": "TestSearchSatisfaction2",
-                       "revision": 15700292
+                       "revision": 15922352
                },
                "schema.GeoFeatures": {
                        "class": "ResourceLoaderSchemaModule",
@@ -154,15 +149,6 @@
                                "mobile"
                        ],
                        "dependencies": []
-               },
-               "ext.wikimediaEvents.didyoumean": {
-                       "scripts": [
-                               "ext.wikimediaEvents.didyoumean.js"
-                       ],
-                       "targets": [
-                               "desktop"
-                       ],
-                       "dependencies": "mediawiki.Uri"
                }
        },
        "ResourceFileModulePaths": {
diff --git a/modules/ext.wikimediaEvents.didyoumean.js 
b/modules/ext.wikimediaEvents.didyoumean.js
deleted file mode 100644
index d2616fd..0000000
--- a/modules/ext.wikimediaEvents.didyoumean.js
+++ /dev/null
@@ -1,110 +0,0 @@
-/*!
- * Javacsript module for measuring usage of the 'did you mean' feature
- * of Special:Search.
- *
- * @license GNU GPL v2 or later
- * @author Erik Bernhardson <ebernhard...@wikimedia.org>
- */
-( function ( mw, $ ) {
-       var uri = new mw.Uri( location.href ),
-               cirrusDYM = uri.query.wprov && uri.query.wprov.indexOf( 
'cirrusDYM-' ) === 0;
-
-       if ( cirrusDYM ) {
-               cirrusDYM = uri.query.wprov;
-               cirrusDYM = cirrusDYM.replace( /^cirrusDYM-/, '' ) + '-click';
-               // cleanup the location bar in supported browsers
-               if ( window.history.replaceState ) {
-                       delete uri.query.wprov;
-                       window.history.replaceState( {}, '', uri.toString() );
-               }
-       } else {
-               cirrusDYM = 'no';
-       }
-
-       function oneIn( populationSize ) {
-               return Math.floor( Math.random() * populationSize ) === 0;
-       }
-
-       function updateHref() {
-               if ( this.id ) {
-                       var uri = new mw.Uri( this.href );
-                       uri.query.wprov = this.id.replace( /^mw-search-DYM/, 
'cirrusDYM' );
-                       this.href = uri.toString();
-               }
-       }
-
-       function participateInTest() {
-               var didYouMean,
-                       $target = $( '.searchdidyoumean' ),
-                       suggestIsRewritten = $target.find( '.searchrewritten' 
).length > 0,
-                       numResults = $( '.mw-search-result-heading' ).length,
-                       pageId = mw.user.generateRandomSessionId(),
-                       runSuggestion = +uri.query.runsuggestion,
-                       logEvent = function ( action ) {
-                               mw.eventLog.logEvent( 'DidYouMean', {
-                                       // Used to correlate actions that 
happen on the same
-                                       // page. Otherwise a user opening 
multiple search
-                                       // results in tabs would have 
overlapping events and we
-                                       // wouldn't know how long between 
visiting the page and
-                                       // clicking something.
-                                       pageId: pageId,
-                                       // The number of normal search results 
shown on the page
-                                       numResults: numResults,
-                                       // Either 'no', 'rewritten' or 
'suggestion' indicating
-                                       // the type of 'did you mean' we served 
to the user
-                                       didYouMean: didYouMean,
-                                       // 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(),
-                                       // Records if the user explicitly opted 
out of auto-running
-                                       // suggested queries
-                                       runSuggestion: isNaN( runSuggestion ) ? 
true : !!runSuggestion,
-                                       // Records whether the user clicked on 
a DYM link to get here
-                                       // 'original-click', 'rewritten-click', 
'suggestion-click', or 'no'
-                                       didYouMeanSource: cirrusDYM,
-                                       // Records the action taken on this page
-                                       action: action
-                               } );
-                       },
-                       attachEvent = function ( selector, action ) {
-                               $target.find( selector ).on( 'click', function 
() {
-                                       logEvent( action );
-                               } );
-                       };
-
-               if ( $target.length === 0 ) {
-                       didYouMean = 'no';
-               } else if ( suggestIsRewritten ) {
-                       didYouMean = 'rewritten';
-               } else {
-                       didYouMean = 'suggestion';
-               }
-
-               logEvent( 'visit-page' );
-               if ( suggestIsRewritten ) {
-                       // showed the user the results of suggested query
-                       // instead of the one they provided.
-                       attachEvent( 'a.searchoriginal', 'clicked-original' );
-                       attachEvent( 'a.searchrewritten', 'clicked-rewritten' );
-               } else {
-                       // suggesting a different query to the user
-                       attachEvent( 'a', 'clicked-did-you-mean' );
-               }
-       }
-
-       $( document ).ready( function () {
-               // tag "Did you mean" suggestion and original query
-               $( '#mw-content-text .searchdidyoumean > a' ).each( updateHref 
);
-               // we fire most events from click handlers, so we need to 
filter for
-               // only browser that support sendBeacon and will reliably 
deliver
-               // these events.
-               if ( navigator.sendBeacon && oneIn( 200 ) ) {
-                       mw.loader.using( [
-                               'mediawiki.user',
-                               'ext.eventLogging',
-                               'schema.DidYouMean'
-                       ] ).then( participateInTest );
-               }
-       } );
-}( mediaWiki, jQuery ) );
diff --git a/modules/ext.wikimediaEvents.searchSatisfaction.js 
b/modules/ext.wikimediaEvents.searchSatisfaction.js
index 12777e2..90f9a78 100644
--- a/modules/ext.wikimediaEvents.searchSatisfaction.js
+++ b/modules/ext.wikimediaEvents.searchSatisfaction.js
@@ -30,8 +30,15 @@
                uri = new mw.Uri( location.href ),
                checkinTimes = [ 10, 20, 30, 40, 50, 60, 90, 120, 150, 180, 
210, 240, 300, 360, 420 ],
                lastScrollTop = 0,
-               articleId = mw.config.get( 'wgArticleId' )
-               ;
+               articleId = mw.config.get( 'wgArticleId' ),
+               // map from dym wprov values to eventlogging inputLocation 
values
+               didYouMeanMap = {
+                       'dym1': 'dym-suggest',
+                       'dymr1': 'dym-rewritten',
+                       'dymo1': 'dym-original',
+               },
+               // some browsers can't do Object.keys properly, so manually 
maintain the list
+               didYouMeanList = [ 'dym1', 'dymr1', 'dymo1' ];
 
        function extractResultPosition( uri, wprovPrefix ) {
                return parseInt( uri.query.wprov &&
@@ -59,6 +66,11 @@
        }
 
        search = initFromWprov( 'srpw1_' );
+       search.didYouMean = uri.query.wprov &&
+               uri.query.wprov.substr( 0, search.wprovPrefix.length ) === 
search.wprovPrefix &&
+               didYouMeanList.indexOf( uri.query.wprov.substr( 
search.wprovPrefix.length ) ) >= 0 &&
+               uri.query.wprov.substr( search.wprovPrefix.length );
+
        autoComplete = initFromWprov( 'acrw1_' );
        // with no position appended indicates the user submitted the
        // autocomplete form.
@@ -74,7 +86,8 @@
                // currently loaded state
                var state = {},
                        storageNamespace = 'wmE-sS-',
-               // persistent state keys that have a lifetime
+               // persistent state keys that have a lifetime. unlisted
+               // keys are not persisted between page loads.
                        ttl = {
                                sessionId: 10 * 60 * 1000,
                                subTest: 10 * 60 * 1000,
@@ -113,7 +126,7 @@
                                        // take the first 52 bits of the rand 
value to match js
                                        // integer precision
                                                parsed = parseInt( rand.slice( 
0, 13 ), 16 );
-                                       return parsed % populationSize === 0;
+                                       return true; //parsed % populationSize 
=== 0;
                                },
                                /**
                                 * Choose a single bucket from a list of 
buckets with even
@@ -398,7 +411,7 @@
                                evt.searchToken = mw.config.get( 
'wgCirrusSearchRequestSetToken' );
                        }
 
-                       // add any schema specific data
+                       // add any action specific data
                        if ( extraData ) {
                                $.extend( evt, extraData );
                        }
@@ -408,6 +421,16 @@
                                eventLog = eventLog || extendMwEventLog();
                                eventLog.logEvent( 'TestSearchSatisfaction2', 
evt );
                        } );
+               };
+       }
+
+       /**
+        */
+       function genAttachWprov( value ) {
+               return function () {
+                       var uri = new mw.Uri( this.href );
+                       uri.query.wprov = value;
+                       this.href = uri.toString();
                };
        }
 
@@ -428,40 +451,86 @@
                        session.refresh( 'sessionId' );
                        session.refresh( 'subTest' );
 
+                       // Standard did you mean suggestion when the user gets 
results for
+                       // their original query
+                       $( '#mw-search-DYM-suggestion' ).each( genAttachWprov(
+                               search.wprovPrefix + 'dym1'
+                       ) );
+
+                       // Link to the current (rewritten) search after we have 
rewritten the original
+                       // query into the did you mean query.
+                       $( '#mw-search-DYM-rewritten' ).each( genAttachWprov(
+                               search.wprovPrefix + 'dymr1'
+                       ) );
+
+                       // Link to the original search after we have rewritten 
the original query
+                       // into the did you mean query
+                       $( '#mw-search-DYM-original' ).each( genAttachWprov(
+                               search.wprovPrefix + 'dymo1'
+                       ) );
+
                        $( '#mw-content-text' ).on(
                                'click',
-                               '.mw-search-result-heading a',
+                               '.mw-search-result-heading a, 
#mw-search-DYM-suggestion, #mw-search-DYM-original, #mw-search-DYM-rewritten',
                                function ( evt ) {
+                                       var wprov,
                                        // Sometimes the click event is on a 
span inside the anchor
-                                       var $target = $( evt.target ).closest( 
'a' ),
-                                               uri = new mw.Uri( $target.attr( 
'href' ) ),
-                                               // Only the primary anchor has 
the data-serp-pos attribute, but we
-                                               // might be updating a sub-link 
like a section.
-                                               index = $target.closest( 
'.mw-search-result-heading' )
-                                                       .find( 
'[data-serp-pos]' )
-                                                       .data( 'serp-pos' );
+                                               $target = $( evt.target 
).closest( 'a' ),
+                                               params = {
+                                                       // Only the primary 
anchor has the data-serp-pos attribute, but we
+                                                       // might be updating a 
sub-link like a section.
+                                                       position: 
$target.closest( '.mw-search-result-heading' )
+                                                               .find( 
'[data-serp-pos]' )
+                                                               .data( 
'serp-pos' )
+                                               };
 
-                                       if ( index !== undefined ) {
-                                               uri.query.wprov = 
search.wprovPrefix + index;
-                                               $target.attr( 'href', 
uri.toString() );
+                                       if ( params.position !== undefined ) {
+                                               wprov = params.position;
+                                       } else if ( $target.is( 
'#mw-search-DYM-suggestion' ) ) {
+                                               wprov = 'dym1';
+                                       } else if ( $target.is( 
'#mw-search-DYM-original' ) ) {
+                                               wprov = 'dymo1';
+                                       } else if ( $target.is( 
'#mw-search-DYM-rewritten' ) ) {
+                                               wprov = 'dymr1';
                                        }
-                                       logEvent( 'click', {
-                                               position: index
-                                       } );
+
+                                       if ( wprov !== undefined ) {
+                                               genAttachWprov( 
search.wprovPrefix + wprov ).apply( $target.get( 0 ) );
+                                       }
+
+                                       // Only log click events for clicks on 
search results, not did you mean
+                                       if ( params.position !== undefined ) {
+                                               logEvent( 'click', params );
+                                       }
                                }
                        );
 
                        params = {
                                query: mw.config.get( 'searchTerm' ),
-                               hitsReturned: $( '.mw-search-result-heading' 
).length
+                               hitsReturned: $( '.mw-search-result-heading' 
).length,
                        };
+
+                       // Track what did you mean suggestions were displayed 
on the page
+                       if ( $( '#mw-search-DYM-suggestion' ).length ) {
+                               params.didYouMeanVisible = 'yes';
+                       } else if ( $( '#mw-search-DYM-rewritten' ).length ) {
+                               params.didYouMeanVisible = 'autorewrite';
+                       } else {
+                               params.didYouMeanVisible = 'no';
+                       }
+
                        // This method is called from jQuery.ready which runs 
on DOMContentLoaded. Use domInteractive since that
                        // is immediately before DOMContentLoaded per spec.
                        if ( window.performance && window.performance.timing ) {
                                params.msToDisplayResults = 
window.performance.timing.domInteractive - 
window.performance.timing.navigationStart;
                        }
+                       if ( search.didYouMean ) {
+                               params.inputLocation = 
didYouMeanMap[search.didYouMean];
+                       }
+
                        logEvent( 'searchResultPage', params );
-               } else if ( search.cameFromSearch ) {
+               }
+               if ( search.cameFromSearch ) {
                        logEvent( 'visitPage', {
                                position: search.resultPosition
                        } );
@@ -533,6 +602,9 @@
                        };
 
                if ( autoComplete.cameFromSearch ) {
+                       // @todo should this still fire if autocomplete sent 
the user
+                       // to Special:Search? This is incredibly common, for 
example,
+                       // for the autocomplete on the main special search page.
                        logEvent( 'visitPage', {
                                position: autoComplete.resultPosition
                        } );
diff --git a/tests/browser/SearchSatisfactionTests.php 
b/tests/browser/SearchSatisfactionTests.php
index fa35bfc..260ba71 100644
--- a/tests/browser/SearchSatisfactionTests.php
+++ b/tests/browser/SearchSatisfactionTests.php
@@ -428,10 +428,44 @@
                        ),
                        // Note that this test requires some page to exist with 
the text 'mani page', or the
                        // did you mean will be rewritten automatically and 
return search results for 'main page'
-                       'full text search click the "did you mean" result' => 
array(
+                       'full text search click the "did you mean" rewritten 
result' => array(
                                // actions
                                array(
                                        $this->visitPage( 
"Special:Search?search=mani%20page" ),
+                                       // if the button is clicked too quickly 
the event doesn't fire because
+                                       // js hasn't loaded.
+                                       $this->sleep( 2 ),
+                                       $this->clickDidYouMeanRewritten(),
+                                       $this->sleep( 2 ),
+                               ),
+                               // expected events
+                               array(
+                                       array( 'action' => 'searchResultPage', 
'source' => 'fulltext', 'position' => null, 'inputLocation' => null, 
'didYouMeanVisible' => 'autorewrite' ),
+                                       array( 'action' => 'searchResultPage', 
'source' => 'fulltext', 'position' => null, 'inputLocation' => 'dym-rewritten', 
'didYouMeanVisible' => 'no' ),
+                               ),
+                       ),
+                       'full text search click the "did you mean" original 
result' => array(
+                               // actions
+                               array(
+                                       $this->visitPage( 
"Special:Search?search=mani%20page" ),
+                                       // if the button is clicked too quickly 
the event doesn't fire because
+                                       // js hasn't loaded.
+                                       $this->sleep( 2 ),
+                                       $this->clickDidYouMeanOriginal(),
+                                       $this->sleep( 2 ),
+                               ),
+                               // expected events
+                               array(
+                                       // @TODO the did you mean should be 
integrated and trigger some click event
+                                       array( 'action' => 'searchResultPage', 
'source' => 'fulltext', 'position' => null, 'inputLocation' => null, 
'didYouMeanVisible' => 'autorewrite' ),
+                                       array( 'action' => 'searchResultPage', 
'source' => 'fulltext', 'position' => null, 'inputLocation' => 'dym-original', 
'didYouMeanVisible' => 'no' ),
+                               ),
+                       ),
+                       'full text search click the "did you mean" suggestion 
result' => array(
+                               // actions
+                               array(
+                                       $this->ensurePage( "Misspelled", "main 
paeg" ),
+                                       $this->visitPage( 
"Special:Search?search=main%20paeg" ),
                                        // if the button is clicked too quickly 
the event doesn't fire because
                                        // js hasn't loaded.
                                        $this->sleep( 2 ),
@@ -440,8 +474,8 @@
                                // expected events
                                array(
                                        // @TODO the did you mean should be 
integrated and trigger some click event
-                                       array( 'action' => 'searchResultPage', 
'source' => 'fulltext', 'position' => null ),
-                                       array( 'action' => 'searchResultPage', 
'source' => 'fulltext', 'position' => null ),
+                                       array( 'action' => 'searchResultPage', 
'source' => 'fulltext', 'position' => null, 'inputLocation' => null, 
'didYouMeanVisible' => 'yes' ),
+                                       array( 'action' => 'searchResultPage', 
'source' => 'fulltext', 'position' => null, 'inputLocation' => 'dym-suggest', 
'didYouMeanVisible' => 'no' ),
                                ),
                        ),
                        'Special:Search bar type then enter' => array(
@@ -742,10 +776,40 @@
                };
        }
 
+       /**
+        * Shown when the original search query was run, but the
+        * search engine has a suggestion for a better query
+        */
        protected function clickDidYouMeanSuggestion() {
                return function ( $webDriver ) {
                        $webDriver->findElement( WebDriverBy::cssSelector(
-                               '.searchdidyoumean a'
+                               '#mw-search-DYM-suggestion'
+                       ) )->click();
+               };
+       }
+
+       /**
+        * Shown when the rewritten search query was run. Gives
+        * the user a direct link to this search, which might show
+        * a new did you mean.
+       */
+       protected function clickDidYouMeanRewritten() {
+               return function ( $webDriver ) {
+                       $webDriver->findElement( WebDriverBy::cssSelector(
+                               '#mw-search-DYM-rewritten'
+                       ) )->click();
+               };
+       }
+
+       /**
+        * Shown when the rewritten search query was run. Gives
+        * the user a direct link to the original search without
+        * it being rewritten.
+       */
+       protected function clickDidYouMeanOriginal() {
+               return function ( $webDriver ) {
+                       $webDriver->findElement( WebDriverBy::cssSelector(
+                               '#mw-search-DYM-original'
                        ) )->click();
                };
        }

-- 
To view, visit https://gerrit.wikimedia.org/r/311654
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I8983a9677ffd9d98c8a347ef0961c2f0e874393f
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/WikimediaEvents
Gerrit-Branch: master
Gerrit-Owner: EBernhardson <ebernhard...@wikimedia.org>

_______________________________________________
MediaWiki-commits mailing list
MediaWiki-commits@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to