Skip to content

Instantly share code, notes, and snippets.

@jdlrobson
Last active Apr 5, 2016
Embed
What would you like to do?
A script for reporting impact of changes
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 =
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' );
}
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
};
@phuedx

This comment has been minimized.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment