Inductiveload User Area
Main User Page Talk Page Talk archives Contributions

WELCOME to my user page. Have a look around my galleries and contributions if you like, or leave messages on my talk page.

If you can suggest improvements to my own work, tell me. Don't let any poor quality work hang around!

Wikisource user page Commons user page Wikibooks user page Wikipedia user page

Languages: English (native), French (basic), German (basic), Mandarin (basic), Mercan (like, totally fluent), Python (passable), C++ (basic), Lua (basic)

Featured status comes only to those prepared to make it, and take it.

The Monthly Challenge for July contains 64 works. You can help by reading the guide and contributing to the current challenge.
This month:
  • Pages processed: 23 (0.8% of target)
  • Avg. pages/day: 23
  • Yesterday: 140

Last month:
  • Pages processed: 3826 (127.5% of target)
  • Avg. pages/day: 128

Ask me to do things


I will need information:

Tools and scripts


User preferences and custom javascripts:

Commons scripts


Popups Reloaded


Popups, but way better.

mw.loader.load('//', "text/css");

Quick Access


Keyboard-driven tool access


Cleanup (Alpha)


This is a much more in-depth version of User:Samwilson/PageCleanUp.js that includes hundreds of regexes for scannos that have unambiguous or almost-certain corrections. For example, no work in English ends in -abte: this is almost certainly -able

This is an alpha-level tool. Configuration, especially, is likely to change.


This tool will work with default configurations, but it is more reliable with configurations.


Quick buttons to load the next/previous pages of a book.

mw.loader.load('// carousel.js&action=raw&ctype=text/javascript');

Save/Load Actions


Run Javascript actions on page save or page load. Can be used to implement custom text transforms, for example from [[Link/Foo|Foo]] to [[Link/Foo|Foo]].

This tool probably needs configuration in your JS. Just adding it won't do anything use. Consult the documentation for more information.

Preview markup

mw.loader.load('//', "text/css");

Maintenance Wizard and Replacer


Perform maintenance without going into edit mode.

// maintain script has no purpose in special
if (mw.config.get("wgCanonicalNamespace") !== "Special") {
  mw.loader.using(['ext.gadget.utils-difference', 'mediawiki.util', 'mediawiki.api',
      'oojs-ui-core', 'oojs-ui-windows', 'oojs-ui-widgets']).done(function() {

Jump to file


Add a button to go to the book file at Commons from the Index or Page namespace, and to the transcluding page from the Page namespace

mw.loader.load('// to file/load.js&action=raw&ctype=text/javascript');



Add a mini image page in the Page namespace for keeping the proofread text nearer the input box.




Add a small popup to assist uploading works at the IA via IA-Upload


Scan Transcludes


Show, in the pagelist grid, which pages are transcluded and which are not, colouring the pages according to expected transclusion status.




Display an icon if a page has been edited recently, with the ability to vary the definition of "recently" on a per-other-user basis.




Show an indicator when a script loads


I use this to check my local script is loading

$(function() {
  $(".mw-indicators").append($("<img src=\"\">"));
$(function() {
  $("#p-coll-print_export a[href*=''").each(function(i, a) {
    $(a).attr("href", $(a).attr("href") + "&nocache=1");

Add a button to the main page POTM to edit it

if (mw.config.get("wgTitle") === "Main Page") {
  $(function() {
    $(".collaboration-potm tr:first-child td:last-child").prepend(
        .css({float: "right", "font-size": "70%"})
        .attr("href", mw.config.get("wgScript") + "?title=Module:PotM/data&action=edit")
$( function () {
  var botname = "InductiveBot";
    '/wiki/Special:Contributions/' + botname,
    'Contributions by ' + botname,
} );
a.external[href^=""] {
    background: url("//") no-repeat right;
    /* @noflip */
    padding-right: 10px;

Add a custom panel of special characters to the WikiEditor toolbar

	var addCharacters = function () {

		$( '#wpTextbox1' ).wikiEditor( 'addToToolbar', {
			section: 'characters',
			pages: {
				yazidi: {
					layout: 'characters',
					label: 'Yazidi',
					characters: [ 'Ḍ', 'ḍ', 'Ḳ', 'ḳ', 'Ṣ', 'ṣ', 'Ḫ', 'ḫ', 'Š', 'š', 'â', 'î' ]
		} );

	/* Check if view is in edit mode and that the required modules are available.
 * Then, customize the toolbar … */
	if ( [ 'edit', 'submit' ].indexOf( mw.config.get( 'wgAction' ) ) !== -1 ) {
		mw.loader.using( 'user.options' ).then( function () {
		// This can be the string "0" if the user disabled the preference
		// ([[phab:T54542#555387]])
			if ( mw.user.options.get( 'usebetatoolbar' ) === 1 ) {
					mw.loader.using( 'ext.wikiEditor' ), $.ready
				).then( function () {
				} );
		} );

Show microformat data under the header

#ws-data {
    display: block !important;

#ws-data > span {
    margin-right: 1ex;
    padding: 0 4px;
    background-color: #e8e8e8;
    border: 1px solid #aaa;
    border-radius: 4px;

"Steal" the preview accesskey (p) for the "preview with this template" button instead

	$( function () {
		var sandboxPrev = $( 'input[name="wpTemplateSandboxPreview"]' );

		if ( sandboxPrev.length ) {
			var ak = 'p';
			// unbind other users of this accessKey
			$ ( '*[accessKey="' + ak + '"]' )
				.attr( 'accessKey', null );

			// add it to our button
			sandboxPrev.attr( 'accessKey', ak );
	} );

Sticky header without hiding all the buttons (Vector)

.skin-vector-legacy #mw-head {
    position: fixed;
    background-image: linear-gradient(to bottom,#ffffff 50%,#f6f6f6 100%);

Browser UserScripts


Add the Hathi IDs to the catalog page tables

// ==UserScript==
// @name Add Id to Hathi Catalog
// @match*
// @version 1.1
// @updateURL
// ==/UserScript==

/* Creates a simple cell element with the given text content */
function create_cell_with_text(tag, text) {
    const cell = document.createElement(tag);
    cell.textContent += text;
    return cell;

window.addEventListener('load', function () {
    const entries = document.querySelectorAll('.viewability-table tr a');

    for (const link of entries) {
        const href = link.getAttribute('href');
        const id = href.split('/').at(-1);
        const row = link.closest('tr');

        row.appendChild(create_cell_with_text('td', id));

    document.querySelector('.viewability-table thead tr')
        .appendChild(create_cell_with_text('th', 'ID'));




List all indexes which have a page with a linter error
python listpages -ns:Page -linter:misnested-tag -format:"Index:{page.title}" | sed -E 's/\/[0-9]+$//' | uniq

Image Processing

Convert all files of type X to type Y, in parallel
find . -type f  -name '*.png' -print0 | parallel -0 convert {} {.}.pbm
Make an image showing only not-black and not-white pixels
convert input.png -colorspace rgb -fill white -opaque black -fill red +opaque output.png

PDF/DJVU processing

Fix a PDF that chokes ImageMagic due to "bad streams"
gs -o generated.pdf -sDEVICE=pdfwrite -dPDFSETTINGS=/prepress input.pdf



Some extra functions that might be handy for other scripts:

The rant zone

In its rightful place, at the bottom of the list.

What does a $130 million budget not get you?

  • phab:T95878 (filed 2015) There's still no mobile editing for Wikidata, after 9 years. Seriously.
    • phab:T259183 The totally-not-beta UI is also still trash on "narrow" screens.
  • phab:T278104 Commons uploading is still totally broken for files over 100MB and no one cares, even though it's blocking IA Upload all the time and also preventing various other uploads Fixed after 7 months.
  • phab:T288980 WVUI Codex might one day get usable by general folk?
  • OOUI is now not only barely documented and horrific to actually use (OK actually it's pretty good from the server side), but also no-one cares because it's going to get "one day, promise" replaced by WVUI
  • VisualEditor (aka the 2017 editor!) is still completely useless, and not just at Wikisource
  • Literally every data model is still work in progress for both WD and SDC and no one cares.
  • phab:T121646 (filed 2016) There's still no API for evicting data from local storage.
  • There's still no guidance for best practices for gadgets. None.
  • The chat ecosystem is completed fragmented into siloed commercial apps and trying to bridge them together is, at best, shunted to "Volunteer time". Just pay for a darn Matrix homeserver like every other FOSS project and stop pretending like everyone will use IRC in 2021 instead of leaving for Discord and Telegram. Even the WMF doesn't really use IRC, it hides its machinations on Slack so no-one can see what the hell is going on.
  • Speaking of, why does Community Tech not work in the open?

But they still need your cash for...something? And the begging banner will use every dark pattern they can fit in there.

And a textbook example of the Mrs Micawber principle (Annual income twenty pounds, annual expenditure nineteen nineteen six, result happiness. Annual income twenty pounds, annual expenditure twenty pounds nought and six, result misery):

Tasks created in (2021-12): 1617
Tasks closed in (2021-12): 1325
Open and stalled tasks in total: 49056

Median age in days of open tasks by priority:

Unbreak now: 24
Needs Triage: 726
High: 1033
Normal: 1601
Low: 2210
Lowest: 2290

The counter-rant zone (aka why maybe it's not all bad)

  • The Special:APISandbox is brilliant
  • The Wikipedia Library is brilliant
  • Commons providing basically infinite storage is brilliant
  • Toolforge and WMCS is brilliant
  • Site Reliability knows where their towels are
  • An API that allows Pywikibot can't be all that bad

Maintenance and reports


Below are lists of pages in Wikisource which are useful for various purposes. All of these could be out of date. If you really need up-to-date reports, just leave me a note, and I will do it as soon as I can.

  • templates A list of all templates in use on enWS, along with links and usage counts.
  • wikisource pages A list of all Wikisource namespace pages.
  • portals A list of all Portal pages.
  • ws-portal redirects A list of all Wikisource pages which redirect to Portal pages. No pages should link to these.
  • ws-wp no backlink A list of Wikisource pages linking to Wikipedia pages which do not link back here.
  • false root pages A list of pages that should be subpages but aren't.
  • site-css-js: in-progress CSS tidying-up - look here for CSS and/or JS moved out of MediaWiki namespace (rather than being deleted)

Bot activities


I operate a bot, InductiveBot, which performs minor maintenance tasks. It is based on pywikipedia and is quite flexible. If you have a specific request, please let me know on my talk page, and I'll see what I can do!

MW dev


Run extension linter

docker-compose exec mediawiki bash -c "cd extensions/ProofreadPage && composer install"
docker-compose exec mediawiki bash -c "cd extensions/ProofreadPage && composer test"
docker-compose exec mediawiki bash -c "cd extensions/ProofreadPage && composer phan"

Run extension parser tests

docker-compose exec mediawiki php tests/parser/parserTests.php --file=extensions/ProofreadPage/tests/parser/proofreadpage_pages_pagelist.txt

Or to run all the tests in a directory:

docker-compose exec mediawiki sh -c 'find extensions/ProofreadPage/tests -name "*.txt" -exec php tests/parser/parserTests.php --file={} \;'

Run extension unit tests

docker-compose exec mediawiki php tests/phpunit/phpunit.php extensions/ProofreadPage/tests/phpunit

Run linter

npm run-script test



PHP: Logging

  • \MWDebug::log('foobar');

PDF munging

Extract page labels from a PDF → JSON

For meanings of /P, /S, /St see §8.3.1 Page Labels in the PDF Spec

qpdf --json --object-streams=disable ss.pdf | jq '[ .pages[] | .label ]'


Transfer an image from Commons to enWS
./ imagetransfer -site:commons:commons -tosite:wikisource:en -keepname -force_if_shared "File:Foobar.djvu"
mw.hook fire points*)
Current lag
All wikis edit firehose
Wikisource Image Uploader uploads at Commons
Zuul Status (Gerrit → Jenkins pipeline)
Thumbor thumbnails - Grafana dashboard
Upload files to Phabricator

Useful things to share


These are some useful scripts I have hacked together. I guarantee nothing! They are certainly not always neatly coded or structured, but they work for quick and dirty jobs.

Tesseract retraining project


Very slowly, I am working on a retraining of some models for Tesseract: Tesseract

General Python scripts


GIMP scripts

  • The best one:
    • Whiten-background.scm Gimp script to remove the background of an image. An adaptation of a script by Leonid Koninin. This is fairly harsh on some images, so use with care.
  • Less good ones:
  • Remove-paper-texture.scm Gimp script to remove the paper background from a scan of a black and white image by a pretty brutal adjustment of the levels
  • Remove-background-colour.scm Gimp script to remove a flat background colour from an image. This is a fairly brutal algorithm, use with care on delicate images. Essentially, this is just a "bundle" of "select colour, erase the colour, and desaturate. It'll likely be better to use one of the above scripts, but this one is fairly instructive from a script-writing point of view.

Bibliographic junk


Data module ecosystem


None of these work fully. Yet.

Core modules
Client modules

One touch template wrapping with Autohotkey


If you use Autohotkey (and you should be), the following is a useful function that lets you wrap the current mouse selection in a template, which saves you having to paste in the contents.

F2 & s :: wrapTemplate("sc") ; small caps

wrapTemplate( name )  
    front :="{{}{{}" . name . "|"
    back :="{}}{}}"  
    wrapTags( front, back) 

wrapTags( front, back ) 
    AutoTrim Off               ; Retain any leading and trailing whitespace on the clipboard.
    ClipSaved := ClipboardAll  ; Save the entire clipboard so we can restore it when we're done
    clipboard =                ; clear the clipboard
    SendInput ^x               ; cut the selection to the clipboard
    ClipWait                   ; wait for the clipboard to contain something
    SendInput %front%%clipboard%%back% ; Output what was selected, surrounded by front and back
    Clipboard := ClipSaved     ; Restore the original clipboard
    ClipSaved =                ; Free the memory in case the clipboard was very large.

Regular expressions

Function Search pattern Replacement Pattern
Remove single newlines. Useful for OCR'd text /([^\n])\n([^\n])/g '$1 $2'
Convert relative links to static links. Useful when putting a TOC in the Page: namespace. (/\[\[\/(.*)\/\]\]/g '\[\[$1\|$1\]\]'

My requests


Works I'm keeping an eye out for.



Technical wishlist


Some things I'd like to see done (that isn't actual proofreading). Some of it is unimportant, some of it may be controversial and un-discussed, but would be nice to address and tighten up.

  • Get dynamic layouts to work for non scan-backed works and scrap {{prose}} and other hard-coded formatting.
  • Fix poem tags - only by having all lines as p or span-tags can we have hanging-indented continuation lines like 95% of all printed poems are. Stanzas should be divs . Might need a whole new tag in the poem extension, but might not be that hard?(???)
  • Train Tesseract specifically for 1700s-style printing esp. with long-s
  • Allow match-and-split to match to PDFs (since there are now ~1m PDFs on Commons)
  • Get para breaks working in OCR loading: phab:T230415
  • Add common fonts:
    • Cursive: phab:T166138
    • JUnicode: phab:T173573
    • Sans Outline (and remove hacks like ℕ𝔼𝕎 𝕐𝕆ℝ𝕂)
    • Serif Outline
    • Maybe a better Polytonic greek?
  • Move MediaWiki:Proofreadpage_index_template to a module
  • Move Template:Header to module


  • Ebook review process leading to categorisation, then... Category:Ready for export
  • Improve index autofill to fetch author links from Commons creator templates: Getting there: Mediawiki:Gadget-Fill Index.js
  • Fix {{FI}} which is invoking full-size images every single time. Merge with and/or deprecate {{large image}}.
  • Fix headers on mobile, the tabular structure is unfriendly on narrow screens (main header done, other namespaces pending main header module-ificaton). See {{header/main block}}