User:Pathoschild/2009 steward confirmation statistics

From Meta, a Wikimedia project coordination wiki
This page statistically summarizes the 2009 steward confirmations. See also a summary of the 2009 elections, and the collected statistics page.

Table[edit]

Unique participants: 227.
This table only shows overall tendency. The final results are determined by discussing the arguments, not counting votes.

Last update: 2009-02-22 01:15 (UTC)

steward support ratio (approximate) oppose reasons result
Spacebirdy 1.000,  100% (73/73) confirmed
DerHexer 1.000,  100% (62/62) confirmed
guillom 1.000,  100% (49/49) confirmed
Angela 1.000,  100% (39/39) confirmed
M7 1.000,  100% (39/39) confirmed
Jon Harald Søby 1.000,  100% (38/38) confirmed
Effeietsanders 1.000,  100% (28/28) confirmed
Pathoschild 0.981,  98.1% (53/54) confirmed confirmed
Anthere 0.975,  97.5% (39/40) inactivity. confirmed
Shanel 0.974,  97.4% (38/39) confirmed
Andre Engels 0.974,  97.4% (37/38) no statement. confirmed
Bastique 0.969,  96.9% (62/64) confirmed
Nick1915 0.969,  96.9% (31/32) confirmed
Drini 0.966,  96.6% (56/58) confirmed
Thogo 0.964,  96.4% (54/56) confirmed
Wpedzich 0.96,  96% (24/25) usurped account on kowiki. confirmed
Lar 0.95,  95% (57/60) confirmed
Dungodung 0.939,  93.9% (46/49) confirmed
Darkoneko 0.925,  92.5% (49/53) confirmed
Millosh 0.917,  91.7% (22/24) exclusion of en-Wikipedians from Global sysop proposal vote. confirmed
Rdsmith4 0.893,  89.3% (25/28) confirmed
Mav 0.846,  84.6% (22/26) inactivity. confirmed
Yann 0.793,  79.3% (23/29) inactivity, frwiki dispute, no statement (later added). confirmed
Walter 0.731,  73.1% (19/26) inactivity. confirmed
Redux 0.722,  72.2% (13/18) inactivity. confirmed
Jusjih 0.571,  57.1% (12/21) inactivity, violation of steward policies, too much access. confirmed
Oscar 0.677,  67.7% (21/31) inactivity. confirmed
Zirland 0.429,  42.9% (9/21) inactivity. failed
Jimbo Wales 0.421,  42.1% (24/57) inactivity, should have 'staff' flag instead. Dear Founder
Cspurrier 0.263,  26.3% (5/19) inactivity. confirmed
Sj 0.111,  11.1% (2/18) inactivity, no statement. confirmed
Dbl2010 0.059,  5.9% (1/17) inactivity. failed
.anaconda 0.000,  0% (0/9) resigned; inactivity, no statement. withdrawn
Paginazero 0.125,  12.5% (1/8) resigned; inactivity. withdrawn
Shizhao 0.000,  0% (0/2) removed (was appointed as ombudsman); violation of steward policies. withdrawn

Script[edit]

Although comments need to be tallied manually (there are no explicit support/oppose sections), this can be done quickly by using the following quick-and-dirty script to count all comments for every listed candidate, split them into oppose/support by manually checking new comments (most comments tend to reflect one side, with a small number of outliers), then sort the table by clicking the provided link.

/*********************
** Functions
*********************/
function ses_report(text) {
	$('stewiestats').adopt(document.createTextNode(text));
}

/*********************
** Initialize
*********************/
function ses_update_stats() {
	/*********************
	** Display status box
	*********************/
	var box = document.getElementById('stewiestats');
	if(!box) {
		// load required framework
		importScriptURI('http://meta.wikimedia.org/w/index.php?title=User:Pathoschild/Scripts/MooTools.js&action=raw&ctype=text/javascript');

		// create box
		box = document.createElement('pre');
		box.setAttribute('id','stewiestats');
		box.setAttribute('style', 'margin:1em; padding:0.5em; border:1px solid #C00;');
		box.appendChild(document.createTextNode('Loading required framework...\n'));
		editbox.parentNode.insertBefore(box, editbox.parentNode.firstChild);

		// jump to box
		window.location.hash = "#stewiestats";
	}

	/*********************
	** Wait until WikiMooTools loaded
	*********************/
	if(!window.$chk) {
		setTimeout('init_scan()', 500);
		return;
	}
	// clear old messages
	box.empty();
	ses_report('Loading required framework...\n');

	/*********************
	** Extract list of candidates & counts
	*********************/
	ses_report('Extracting candidate list...\n');

	var names = [];
	var totals = [];

	var text  = editbox.value.match(/<!--ses-start-->([\s\S]+?)<!--ses-end-->/)[1].toString();
	var temps = text.match(/{{user[\s\S]+?}}/ig);
	for(var i=0, len=temps.length; i<len; i++) {
		var supps = temps[i].match(/support *= *(.+)/)[1].toString();
		var oppos = temps[i].match(/oppose *= *(.+)/)[1].toString();

		names[i]  = temps[i].match(/name *= *(.+)/)[1].toString();
		totals[i] = parseInt(supps) + parseInt(oppos);
	}

	/*********************
	** Extract participant count
	*********************/
	ses_report('Counting unique participants... ');
	box.adopt(
		new Element('span', {'id':'ses-box-count'})
	);
	ses_report('\n');

      	var request = new Request({
      		url:'http://meta.wikimedia.org/wiki/Stewards/confirm?action=render',
      		method: 'GET',
      		onSuccess:function(text, xml) {
			var links = text.match(/title="User:[^\/"]+/ig);
			var names = {};
			var count = 0;

			for(var i=0, len=links.length; i<len; i++) {
				var name = links[i].match(/User:(.+)/)[1];
				if(!names[name]) {
					names[name] = 1;
					count++;
				}
			}
			$('ses-box-count').adopt(document.createTextNode(count));
			editbox.value = editbox.value.replace(/(<span id="ses_count">).*?(<\/span>)/, '$1' + count + '$2');
      		},
      		onFailure: function(xhr) {
			$('ses-box-count').set({'text':' FAILED: ' + xhr.status + ' (' + xhr.statusText + ')'});
      		}
      	}).send();

	/*********************
	** Extract comment counts per candidate
	*********************/
	ses_report('Fetching elections pages...\n');
	for(var i=0, len=temps.length; i<len; i++) {
		var div = new Element('div')
		box.adopt(div);
		extract_count(div, names[i], totals[i]);
	}

	/*********************
	** Extract a count
	*********************/
	function extract_count(div, name, prev_count) {
		var request = new Request({
			url:'http://meta.wikimedia.org/wiki/Stewards/confirm/2009/' + encodeURIComponent(name) + '?action=render',
			method: 'GET',
			onSuccess:function(text, xml) {
				// strip cruft
				text = text.replace(/^[\s\S]+?Comments about .+/, '');	// statement, etc
  				text = text.replace(/^<([ou]l) [^>]*class="[^"]*stat-ignore[\s\S]+?<\/\1>/mg, ''); // ignore non-comment bullets
  				text = text.replace(/^<(?:ul|li).+class\s*=\s*"\s*neutral.+/mig, ''); // ignore neutrals

				// count comments
				var count = text.match(/^<li/mg).length;

				// report to user
				div.set({
					'text':'   ' + (count<10 ? ' ' : '') + count + ' '
				}).adopt(
					new Element('a', {
						'href':'http://meta.wikimedia.org/wiki/Stewards/confirm/2009/' + encodeURIComponent(name) + '?action=render',
						'text':name
					})
				);

				// highlight those with non-matching counts
				if(count!=prev_count)
					div.set({'styles':{'background':'#FEE', 'font-weight':'bold'}});
				else
					div.set({'styles':{'color':'gray'}});

			},
			onFailure: function(xhr) {
				div.set({'text':'   ' + name + ' FAILED: ' + xhr.status + ' (' + xhr.statusText + ')'});
			}
		}).send();
	}

	/*********************
	** Add 'sort table' link
	*********************/
	box.adopt(
		new Element('div').adopt(
			new Element('a', {
				'href':'javascript:ses_sort_table();',
				'text':'sort table'
			})
		)
	);
}
ses_update_stats();

/*********************
** Sort table
*********************/
function ses_sort_table() {
	ses_report('\nSorting table and updating date...\n');
	/***************
	** Extract rows & data
	***************/
	var raw = editbox.value.match(/<!--ses-start-->([\s\S]+?)<!--ses-end-->/i)[1].toString();
	raw     = raw.match(/{{user[^}]+?}}/g);

	var names  = [];
	var sorted = [];
	var rows   = {};
	var ratios = {};
	var counts = {};

	for(var i=0, len=raw.length; i<len; i++) {
		var name     = raw[i].match(/name *= *(.+)/)[1].toString();
		var supports = raw[i].match(/support *= *(.+)/)[1].toString();
		var opposes  = raw[i].match(/oppose *= *(.+)/)[1].toString();

		names.push(name);
		counts[name] = supports+opposes;
		ratios[name] = supports/(supports+opposes);
		rows[name] = raw[i];
	}

	/***************
	** Sort rows
	***************/
	for(var i=0, len=names.length; i<len; i++) {
		var name  = names[i];
		var ratio = ratios[name];

		// insert into sorted position
		for(var x=0, slen=sorted.length; x<=slen; x++) {
			// end of array, just push it in
			if(x==slen) {
				sorted.push(name);
				break;
			}
			// equal but more votes (or sorted first), insert
			else if(ratios[name]==ratios[sorted[x]]) {
				if(counts[name]>counts[sorted[x]] || (counts[name]==counts[sorted[x]] && name<sorted[x])) {
					sorted.splice(x, 0, name);
					break;
				}
			}
			// higher, insert
			else if(ratios[name]>ratios[sorted[x]]) {
				sorted.splice(x, 0, name);
				break;
			}
		}
	}

	/*********************
	** Update date
	*********************/
	function get_date() {
		// zero pad
		function zero_pad(num) {
			if(num<10)
				num = '0' + num;
			return num;
		}

		// generate string
		var d = new Date();
		var str = d.getUTCFullYear() + '-' + zero_pad(d.getUTCMonth()+1) + '-' + zero_pad(d.getUTCDate()) + ' '
		        + zero_pad(d.getUTCHours()) + ':' + zero_pad(d.getUTCMinutes()) + ' (UTC)';
		return str;
	}
	editbox.value = editbox.value.replace(/(<span id="ses_date">).*?(<\/span>)/, '$1' + get_date() + '$2');

	/***************
	** Update edit box
	***************/
	var str = '';
	len = sorted.length;
	for(var i=0; i<len; i++)
		str += rows[sorted[i]] + '\n';
	editbox.value = editbox.value.replace(/(<!--ses-start-->)[\s\S]+?(<!--ses-end-->)/i, '$1\n' + str + '$2');

	document.getElementById('wpSummary').value = 'updated';
	ses_report('Done.');
}