Skip to content

Instantly share code, notes, and snippets.

@jdlrobson jdlrobson/README.txt
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
You can’t perform that action at this time.