User:Jeblad/pageview/script.js

From Meta, a Wikimedia project coordination wiki

Note: After publishing, you may have to bypass your browser's cache to see the changes.

  • Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
  • Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
  • Internet Explorer / Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5
  • Opera: Press Ctrl-F5.
// Code to make a footer message about pageviews
// © John Erling Blad, Creative Commons by Attribution 3.0

// this needs slight editing if used as a gadget,
// ie. remove first and last line and make a line in [[MediaWiki:Gadgets-definition]]
mw.loader.using( ['mediawiki.api'], function () {
$(function( $content ) {
	"use strict";
	var days = 14;
	
	var conf = mw.config.get( [
		'wgUserLanguage',
		'wgNamespaceNumber',
		'wgServerName',
		'wgPageName'
	] );
	
	if ( conf.wgNamespaceNumber !== 0) {
		mw.log( "So wise so young, they say do never live long." );
		return;
	}
	
	var ajax = {
		'protocol': mw.Uri.protocol,
		'host': 'wikimedia.org',
		'path': '/api/rest_v1/metrics/pageviews/per-article'
	};
	
	var buildURI = function(ajax, opts) {
		var args = jQuery.extend({}, ajax, opts);
		var path = ajax.path.split('/');
		if (args.project === undefined) {
			args.project = conf.wgServerName;
		}
		if (/^[\w\.]*$/.test(args.project)) path.push(args.project);
		if (args.access === undefined) {
			args.access = 'all-access';
		}
		if (/^[-\w]*$/.test(args.access)) path.push(args.access);
		if (args.agents === undefined) {
			args.agents = 'all-agents';
		}
		if (/^[-\w]*$/.test(args.agents)) path.push(args.agents);
		if (args.pageName === undefined) {
			args.pageName = conf.wgPageName;
			path.push(args.pageName);
		}
		else {
			if (/^[-+_\w\s%]*$/i.test(args.pageName)) path.push(args.pageName);
		}
		if (args.granularity === undefined) {
			args.granularity = 'daily';
		}
		if (/^[\w]*$/.test(args.granularity)) path.push(args.granularity);
		var end = new Date();
		var start = new Date(end.valueOf()-(days+1)*24*60*60*1000);
		if (args.start === undefined) {
			args.start = ''
				+start.getUTCFullYear()
				+(start.getUTCMonth()<10 ? '0' : '')+(start.getUTCMonth()+1)
				+(start.getUTCDate()<10 ? '0' : '')+start.getUTCDate(); 
		}
		if (/^[\d]*$/i.test(args.start)) path.push(args.start);
		if (args.end === undefined) {
			args.end = ''
				+end.getUTCFullYear()
				+(end.getUTCMonth()<10 ? '0' : '')+(end.getUTCMonth()+1)
				+(end.getUTCDate()<10 ? '0' : '')+end.getUTCDate(); 
		}
		if (/^[\d]*$/i.test(args.end)) path.push(args.end);
		return new mw.Uri( jQuery.extend({}, args, { 'path': path.join('/')}) );
	};
	
	var stats = {
		count: 0,
		mean: 0,
		stdev: 0,
		mean2: 0,
		stdev2: 0,
		day: 0
	};
	
	function plain() {
		mw.log('doing something plain');
		$('#footer-info')
			.prepend(
				$('<li>')
					.attr({'id':'footer-info-viewcount'})
					.text(mw.message( 'viewcount', stats.day ).text()));
	}
	
	function fancy() {
		mw.log('doing something fancy');
		mw.log(stats);
		var stdevMsg = 'pageview-normal-stdev';
		if (stats.mean > 3*stats.stdev) {
			stdevMsg = 'pageview-small-stdev';
		}
		else if (stats.mean < stats.stdev) {
			stdevMsg = 'pageview-large-stdev';
		}
		var changeMsg = 'pageview-steady-state';
		if (stats.day > stats.mean + 3*stats.stdev) {
			changeMsg = 'pageview-full-climb';
		}
		else if (stats.day < stats.mean - 3*stats.stdev) {
			changeMsg = 'pageview-full-drop';
		}
		else if (stats.day > stats.mean + stats.stdev) {
			changeMsg = 'pageview-slight-climb';
		}
		else if (stats.day < stats.mean - stats.stdev) {
			changeMsg = 'pageview-slight-drop';
		}
		var text = mw.message( 'pageview-count', stats.day ).text();
		text += ' ';
		text += mw.message( stdevMsg, Math.round(stats.stdev), Math.round(100*stats.stdev/stats.mean) ).text();
		text += ' ';
		text += mw.message( changeMsg, Math.round(100*(stats.day-stats.mean)/stats.mean), Math.round(stats.mean), stats.count ).text();
		if (stats.count<days) {
			text += ' ';
			text += mw.message( 'pageview-missing-days', days - stats.count, Math.round(stats.mean2), Math.round(stats.stdev) ).text();
		}
		text += ' ';
		text += mw.message( 'pageview-help' ).parse();
		$('#footer-info')
			.prepend(
				$('<li>')
					.attr({'id':'footer-info-viewcount'})
					.html(text));
	}
	
	function render() {
		return mw.message('pageview-count').exists() ? fancy() : plain();
	}
	
	var statsRequest = $.ajax({
		'cache':true,
		'dataType': 'json',
		'url': buildURI(ajax, {}) })
		.done(function(data){
			mw.log(data);
			var num = 0;
			var meanAcc = 0;
			var sqrAcc = 0;
			$.each( data.items, function ( index, item ) {
				if (index!==0 && num < days) {
					meanAcc += item.views;
					sqrAcc += Math.pow(item.views, 2);
					num++;
				}
			});
			stats.day = data.items[0].views;
			stats.count = num;
			stats.mean = meanAcc/num;
			stats.mean2 = meanAcc/days;
			stats.stdev = Math.sqrt((sqrAcc)/num - Math.pow(meanAcc/num, 2));
			stats.stdev2 = Math.sqrt((sqrAcc)/days - Math.pow(meanAcc/days, 2));
		});
	
	var msgs = {
		'viewcount':null,
		'pageview-count':'The page is shown {{PLURAL:$1|one time|$1 times}} last 24 hours.',
		'pageview-small-stdev':'The page has a small variation in pageviews, only  {{PLURAL:$1|one pageview|$1 pageviews}} or {{PLURAL:$2|one percent|$2 %}} of mean.',
		'pageview-normal-stdev':'The page has a normal variation in pageviews, about {{PLURAL:$1|one pageview|$1 pageviews}} or {{PLURAL:$2|one percent|$2 %}} of mean.',
		'pageview-large-stdev':'The page has a large variation in pageviews, about {{PLURAL:$1|one pageview|$1 pageviews}} or {{PLURAL:$2|one percent|$2 %}} of mean.',
		'pageview-full-climb':'It is climbing fast, relative to the variation in pageviews, with {{PLURAL:$1|one percent|$1 %}} change from a mean of {{PLURAL:$2|one pageview|$2 pageviews}} daily over previous {{PLURAL:$3|day|$3 days}}.',
		'pageview-slight-climb':'It is climbing slowly, relative to the variation in pageviews, with {{PLURAL:$1|one percent|$1 %}} change from a mean of {{PLURAL:$2|one pageview|$2 pageviews}} daily over previous {{PLURAL:$3|day|$3 days}}.',
		'pageview-steady-state':'It is in a steady state, relative to the variation in pageviews, with {{PLURAL:$1|one percent|$1 %}} change from a mean of {{PLURAL:$2|one pageview|$2 pageviews}} daily over previous {{PLURAL:$3|day|$3 days}}.',
		'pageview-slight-drop':'It is dropping slowly, relative to the variation in pageviews, with {{PLURAL:$1|one percent|$1 %}} change from a mean of {{PLURAL:$2|one pageview|$2 pageviews}} daily over previous {{PLURAL:$3|day|$3 days}}.',
		'pageview-full-drop':'It is dropping fast, relative to the variation in pageviews, with {{PLURAL:$1|one percent|$1 %}} change from a mean of {{PLURAL:$2|one pageview|$2 pageviews}} daily over previous {{PLURAL:$3|day|$3 days}}.',
		'pageview-missing-days':'There {{PLURAL:$1|is|are}} no data for {{PLURAL:$1|one day|$1 days}}, and if {{PLURAL:$1|that day is|those days are}} included the mean would be {{PLURAL:$2|one pageview|$2 pageviews}} daily and standard variation would be {{PLURAL:$3|one pageview|$3 pageviews}}.',
		'pageview-help':'More information at [[m:Special:MyLanguage/Help:Pageviews|help for pageviews]].'
	};
	var api = new mw.Api();
	var amRequest = api.ajax({
		action: 'query',
		meta: 'allmessages',
		amlang: conf.wgUserLanguage,
		ammessages: Object.keys(msgs)
	})
	.done(function(data){
		$.each( data.query.allmessages, function ( index, message ) {
			if ( message.missing !== '' ) {
				mw.messages.set( message.normalizedname, message['*'] );
			} else if (msgs[message.name]) {
				mw.messages.set( message.normalizedname, msgs[message.name] );
			}
		});
	});
	
	mw.hook( 'wikipage.content' ).add(function( $content ) {
		Promise.all([amRequest, statsRequest]).then(render);
	});
});
});