A script for reporting impact of changes
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Usage: | |
Beta cluster (beta) | |
node wptreporter.js "webpagetest.enwiki-bc-mobile-2gslow.anonymous.Barack_Obama.us-east-1.Google_Chrome-emulateMobile.firstView" 3 2 2016 21 0 "" 10 wikitext=yes | |
Beta cluster (stable) | |
node wptreporter.js "webpagetest.enwiki-bc-mobile-beta-2gslow.anonymous.Barack_Obama.us-east-1.Google_Chrome-emulateMobile.firstView" 4 2 2016 22 16 "" 10 | |
Production (beta) | |
node wptreporter.js "webpagetest.enwiki-mobile-beta-2gslow.anonymous.Barack_Obama.us-east-1.Google_Chrome-emulateMobile.firstView" 9 2 2016 22 0 "" 10 | |
node wptreporter.js "frontend.navtiming.totalPageLoadTime.mobile-beta.anonymous" 9 2 2016 22 0 "median" "" 10 | |
node wptreporter.js "frontend.navtiming.firstPaint.mobile-beta.anonymous" 9 2 2016 22 0 "median" "" 10 | |
node pageviews 9 2 2016 22 | |
# Before change pageviews = https://wikimedia.org/api/rest_v1/metrics/pageviews/aggregate/en.wikipedia/mobile-web/user/daily/2016020300/2016021000 | |
# After change pageviews = |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var fetch = require( 'node-fetch' ); | |
var args = process.argv.slice(2); | |
function pad( num ) { | |
if ( num < 10 ) { | |
return '0' + num; | |
} else { | |
return new String( num ); | |
} | |
} | |
function report( day, month, year, hour, windowInDays ) { | |
windowInDays = parseInt( windowInDays || 10, 10 ); | |
console.log( '|Before daily page view (avg)|', 'After daily page view (avg)|', 'Delta (Avg)|', '% increase (Avg)|' ); | |
var d = new Date( year, month, day, hour ); | |
var change = [ d.getFullYear(), pad( d.getMonth() ), pad( d.getDate() ), pad( d.getHours() ) ].join( '' ); | |
d.setDate( d.getDate() - windowInDays ); | |
var before = [ d.getFullYear(), pad( d.getMonth() ), pad( d.getDate() ), pad( d.getHours() ) ].join( '' ); | |
d = new Date( year, month, day, hour ); | |
d.setDate( d.getDate() + windowInDays ); | |
if ( d > new Date() ) { | |
d = new Date(); | |
} | |
var after = [ d.getFullYear(), pad( d.getMonth() + 1 ), pad( d.getDate() ), pad( d.getHours() ) ].join( '' ); | |
var url = 'https://wikimedia.org/api/rest_v1/metrics/pageviews/aggregate/en.wikipedia/mobile-web/user/daily/' + before + '/' + change; | |
var urlAfter = 'https://wikimedia.org/api/rest_v1/metrics/pageviews/aggregate/en.wikipedia/mobile-web/user/daily/' + change + '/' + after; | |
function avg( json ) { | |
var total = 0; | |
json.items.forEach( function ( item ) { | |
total += item.views; | |
} ); | |
return total / json.items.length; | |
} | |
fetch( url ).then( function ( resp ) { | |
return resp.json(); | |
} ).then( function ( json ) { | |
var before = avg( json ); | |
fetch( urlAfter ).then( function ( resp ) { | |
return resp.json(); | |
} ).then( function ( json ) { | |
var after = avg( json ); | |
console.log( [ | |
before, after, after - before, ( (after - before ) / after ) * 100 | |
].join( '|' ) ); | |
} ); | |
return true; | |
} ); | |
} | |
if ( args[0] ) { | |
report.apply( this, args ); | |
} else { | |
console.log( 'Missing arg. Use like: node index.js dd mo yyyy hh 10' ); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var fetch = require( 'node-fetch' ); | |
var args = process.argv.slice(2); | |
var wikiTextOutput = args.indexOf( 'wikitext=yes' ) > -1; | |
function pad( num ) { | |
if ( num < 10 ) { | |
return '0' + num; | |
} else { | |
return new String( num ); | |
} | |
} | |
function getMedian( values ) { | |
var mid = Math.floor( values.length / 2 ); | |
values = values.sort( function ( a, b ) { | |
return a < b ? -1 : 1; | |
} ); | |
if ( values.length % 2 ) { | |
return values[mid]; | |
} else { | |
return ( values[mid-1] + values[mid] ) / 2; | |
} | |
} | |
function toTs( d ) { | |
return pad( d.getHours() ) + ':' + pad( d.getMinutes() ) + '_' + d.getFullYear() | |
+ pad( d.getMonth() + 1 ) + pad( d.getDate() ); | |
} | |
function reportProperty( bucket, property, day, month, year, hour, minute, windowInDays ) { | |
var start_ts, ts_pre_change, ts_of_change, ts_end_test; | |
// By default we use a 10 day window before and after the change (unless specified) | |
windowInDays = parseInt( windowInDays, 10 ) || 10; | |
var d = new Date( year, month-1, day, hour, minute ); | |
d.setDate( d.getDate() - windowInDays ); | |
start_ts = toTs( d ); | |
d = new Date( year,month-1,day, hour, minute ); | |
d.setMinutes( d.getMinutes() - 5 ); | |
ts_pre_change = toTs( d ); | |
d = new Date( year, month-1, day, hour, minute ); | |
ts_of_change = toTs( d ); | |
d = new Date( year, month-1, day, hour, minute ); | |
d.setDate( d.getDate() + windowInDays ); | |
ts_end_test = toTs( d ); | |
var beforeChangeUrl = 'http://graphite.wikimedia.org/render?target=' + bucket + '.' + property + '&from=' + start_ts + '&until=' + ts_pre_change + '&format=json' | |
var afterChangeUrl = 'http://graphite.wikimedia.org/render?target=' + bucket + '.' + property +'&from=' + ts_of_change +'&until=' + ts_end_test +'&format=json'; | |
return fetch( beforeChangeUrl ).then( function ( resp ) { | |
return resp.json(); | |
} ).then( function( json ) { | |
var sum = 0; | |
var values = []; | |
json[0].datapoints.forEach( function ( item ) { | |
if ( item[0] ) { | |
sum += item[0]; | |
values.push( item[0] ); | |
} | |
} ); | |
var medianBefore = getMedian( values ); | |
var avgBefore = sum / values.length; | |
fetch( afterChangeUrl ).then( function ( resp ) { | |
return resp.json(); | |
} ).then( function( json ) { | |
values = []; | |
sum = 0; | |
json[0].datapoints.forEach( function ( item ) { | |
if ( item[0] ) { | |
values.push( item[0] ); | |
sum += item[0]; | |
} | |
} ); | |
var avgAfter = sum / values.length; | |
var medianAfter = getMedian( values ); | |
var avgDelta = avgBefore - avgAfter; | |
var medianDelta = medianBefore - medianAfter; | |
var result = [ | |
property, | |
avgBefore.toFixed( 1 ), | |
avgAfter.toFixed( 1 ), | |
avgDelta.toFixed( 1 ), | |
( avgDelta * 100 / avgBefore ).toFixed( 2 ) + '%', | |
medianBefore.toFixed( 1 ), | |
medianAfter.toFixed( 1 ), | |
medianDelta.toFixed( 1 ), | |
( medianDelta * 100 / medianBefore ).toFixed( 2 ) + '%' | |
]; | |
if ( wikiTextOutput ) { | |
console.log( '|-\n|' + result.join( '||' ) ); | |
} else { | |
console.log( '|' + result.join( '|' ) ); | |
} | |
} ); | |
} ); | |
} | |
function report( bucket, day, month, year, hour, minute, property, windowInDays ) { | |
windowInDays = parseInt( windowInDays, 10 ); | |
console.log( bucket, ':\n' ); | |
var header = [ 'Property', 'Before (avg)', 'after (avg)', 'Delta (Avg)', '% decrease (Avg)', | |
'Before (median)', 'After (median)', 'Delta (median)', '% decrease (median)' ]; | |
if ( wikiTextOutput ) { | |
console.log( '{| class="wikitable"\n|-\n!' + header.join( '!!' ) ); | |
} else { | |
console.log( '|' + header.join( '|' ) ); | |
} | |
if ( property ) { | |
reportProperty( bucket, property, day, month, year, hour, minute, windowInDays ); | |
} else { | |
reportProperty( bucket, 'html.bytes', day, month, year, hour, minute, windowInDays ).then( function ( resp ) { | |
return reportProperty( bucket, 'TTFB.median', day, month, year, hour, minute, windowInDays ); | |
} ).then( function () { | |
return reportProperty( bucket, 'render.median', day, month, year, hour, minute, windowInDays ); | |
} ).then( function () { | |
return reportProperty( bucket, 'fullyLoaded.median', day, month, year, hour, minute, windowInDays ); | |
} ); | |
} | |
} | |
if ( args[0] ) { | |
report.apply( this, args ); | |
} else { | |
console.log( 'Missing arg. Use like: node index.js "enwiki-bc-mobile-2gslow.anonymous.Barack_Obama" dd mo yyyy hh mm <property> <period (days)> <output wikitext>' ); | |
} | |
module.exports = { | |
pad: pad | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
When in doubt search npm (!):