User:Ahecht/sandbox/Scripts/draft-sorter.js

//jshint maxerr:512 //jshint esnext:false //jshint esversion:8

//Based on User:Enterprisey/draft-sorter.js ( function ( $, mw ) { mw.loader.using( ["mediawiki.api", "jquery.chosen", "oojs-ui-core"], function { mw.loader.load( "mediawiki.ui.input", "text/css" );

if ( mw.config.get( "wgNamespaceNumber" ) !== 118 ) { if ( mw.util.getParamValue('draftsorttrigger') ) { // "Next draft" was clicked, but we ended up on a non-draft page nextDraft; return; } else { return; }	}

var portletLink = mw.util.addPortletLink("p-cactions", "#", "Sort draft (sandbox)", "pt-draftsort", "Manage WikiProject tags"); $( portletLink ).click( function ( e ) {		e.preventDefault;

// If it's already there, don't duplicate if ( $( "#draft-sorter-wrapper" ).length ) { return; } // Configure defaults //var templateCache = mw.config.get("wgFormattedNamespaces")[2]+":"+mw.config.get("wgUserName")+"/Scripts/draft-sorter.json"; var templateCache = "Wikipedia:WikiProject Articles for creation/WikiProject templates.json"; // Define the form var form = $( " " ) .attr( "id", "draft-sorter-wrapper" ) .css( { "background-image": "url(https://upload.wikimedia.org/wikipedia/commons/e/e2/OOjs_UI_icon_tag-ltr-progressive.svg)",					"background-repeat": "no-repeat",					"background-position-y": "center",					"background-size": "50px",					"min-height": "50px",					"margin": "1em auto",					"border": "thin solid #BBB",					"padding": "0.5em 50px",					"display": "inline-block",					"border-radius": "0.25em"			} ).append( $( " " )				.text( "Loading form..." )				.css( "color", "gray" )			); // Add the form to the page form.insertAfter( "#contentSub" );

var select = $( " " ) .attr( "id", "draft-sorter-form" ) .attr( "multiple", "multiple" ); var submitButton = new OO.ui.ButtonWidget .setLabel( "Submit" ) .setFlags( [ 'primary', 'progressive' ] ) .on("click", function ( e ) { submit; } ); var cancelButton = new OO.ui.ButtonWidget .setLabel( "Cancel" ) .setFlags( ["destructive"] ) .on("click", function( e ) {					$( "#draft-sorter-wrapper" ).remove;					window.location.replace( window.location.href.replace("draftsorttrigger=y","") );				} );

var nextButton = new OO.ui.ButtonWidget .setIcon( "next" ) .setLabel( "Skip" ) .on("click", function ( e ) { nextDraft; } );

// Determine what templates are already on the talk page var existingProjects = []; var wikiprojects = {};

new mw.Api.get( {				action: "query",				titles: "Draft talk:" + mw.config.get( "wgTitle" ),				generator: "templates",				redirects: "1",				gtllimit: "max",		} ).done (function (data) {			if (data && data.query && data.query.pages) {				$.each(data.query.pages, function (i) { var item = data.query.pages[i].title.match(/^Template:(WikiProject\s[^\/]*)$/i); if (item && item[1] && item[1] != "WikiProject banner shell") { existingProjects.push(item[1]); }				} );			}			console.log( "Project templates found on talk page: ");			console.log( existingProjects );			fetchJSONList(templateCache).then( (cachedList) => { wikiprojects = cachedList; constructForm; } );		} ).fail (function {			console.log("Retrieving project templates from talk page failed.");			fetchJSONList(templateCache).then( (cachedList) => { wikiprojects = cachedList; constructForm; } );		});		predicts = []; async function fetchJSONList(listName) { var parsedList = {}, listData; var query = { action:'parse', prop:'wikitext', page: listName, formatversion: '2', origin: '*' };			try { listData = await new mw.Api.get( query ); } catch (jsonerror) { console.warn("Unable to fetch contents of " + listName + ":"); console.log(jsonerror); }			if (listData && listData.parse && listData.parse.wikitext) { try { parsedList = JSON.parse(listData.parse.wikitext); } catch (jsonerror) { console.warn("Error parsing JSON list " + listName + ":"); console.log(jsonerror); }			}			return parsedList; }		function nextDraft { // Special:RandomInCategory isn't random, use toolforge instead if (nextButton) { nextButton.setLabel( "Loading..." ).setDisabled( true ); }			window.location.href = "https://randomincategory.toolforge.org/Pending_AfC_submissions?draftsorttrigger=y&cmnamespace=118&cmtype=page&returntype=subject&server=" + mw.config.get("wgServerName"); }		function showPredicts { $( "#draft-sorter-status" ).append( "Suggested categories from ORES:" ); predicts.forEach( function(item) { 				function addWithLink(p) {					$( "#draft-sorter-suggest" ).append( $( "" ).text( item + " (" ).append( $( "" ).text("add").click(								function {									$( select ).val( $( select ).val.concat( [ "WikiProject " + p ] ) ).trigger("chosen:updated");								}							) ).append( ")" ) );				}				var singularItem = item.replace(/s$/, '');				if( !existingProjects.includes( "WikiProject " + item ) && wikiprojects[item] ) { //Prediction matches a WikiProject and doesn't already exist					addWithLink(item);				} else if( singularItem != item && !existingProjects.includes( "WikiProject " + singularItem ) && wikiprojects[singularItem] ) { //Singular form of prediction matches a WikiProject and doesn't exist					addWithLink(singularItem);				} else { //Prediction doesn't match a WikiProject or already exists					$( "#draft-sorter-suggest" ).append( $( "" ).append( item ) );				}			} );			return; }		function getPredicts { var lang = mw.config.get("wgServerName").split(".wikipedia.org"); if (lang.length == 1) return; const liftWingExternalEndpoint = "https://api.wikimedia.org/service/lw/inference/v1/models/"; let headers = new Headers({			   "Content-Type": "application/json",				"User-Agent": "draft-sorter (https://en.wikipedia.org/wiki/User:Ahecht/Scripts/draft-sorter.js)"			}); var revID = mw.config.get( "wgCurRevisionId" ); var model = (lang[0] == "en") ? "enwiki-drafttopic" : "outlink-topic-model"; var postBody = JSON.stringify({				"rev_id": revID,				"lang": lang[0],				"page_title": mw.config.get("wgPageName")			});

fetch(liftWingExternalEndpoint + model + ":predict", {				method: "POST",				headers: new Headers({ "Content-Type": "application/json", "User-Agent": "draft-sorter (https://en.wikipedia.org/wiki/User:Ahecht/Scripts/draft-sorter.js)" }),				body: postBody			}).then(response => response.json).then(data => {				var prediction = [];				var dbName = mw.config.get("wgDBname");				if(data && data[dbName] && data[dbName].scores && data[dbName].scores[revID] && data[dbName].scores[revID].drafttopic && data[dbName].scores[revID].drafttopic.score && data[dbName].scores[revID].drafttopic.score.prediction) {					prediction = data[dbName].scores[revID].drafttopic.score.prediction;				} else if (data && data.prediction && data.prediction.results) {					data.prediction.results.forEach( p => { if (p && p.topic) prediction.push(p.topic); } );				} 				if (prediction.length) {					console.log("Got ORES response! Raw predictions:");					console.log(prediction);					prediction.forEach( function (item) { var last = item.split(".")[item.split(".").length-1]; var penultimate = item.split(".")[item.split(".").length-2]; if ( last.substr(-1) == "*" ) { // Filter out redundant starred predictions if (prediction.find(element => (								element.split(".")[element.split(".").length-1] != last &&								element.split(".")[element.split(".").length-2] == penultimate							) ) ) { console.log("Prediction \"" + last + "\" excluded."); last = null; } else { last = penultimate; }						}						if ( wikiprojects[last] ) { // WikiProject found, no need to try splitting predicts.push(last); } else if ( last ) { // Can't find wikiProject, try splitting var splitLast = last.split(/( & | and )/); for (i=0;i<=splitLast.length;i+=2) { splitLast[i] = splitLast[i].charAt(0).toUpperCase + splitLast[i].slice(1); predicts.push( splitLast[i] ); }						}					} );					console.log("Filtered predictions:");					console.log(predicts);					showPredicts;				} else {					console.warn("Error finding predictions in ORES response:");					console.warn(data);				}			} ).catch( e => console.warn("Error retrieving ORES data: " + e) );

return; }

// Construct the form function constructForm { mw.loader.load( "oojs-ui.styles.icons-movement"); Object.keys(wikiprojects).sort.forEach( function(name) {				select.append( $( " " ) .attr( "value", wikiprojects[name] ) .text( name ) );			} ); form.hide; form.empty; form.append( $( " " )				.text( "Tag WikiProjects: " )				.css( { "font-size": "115%", "font-weight": "bold" } )			);			form.append( select ); form.append( "&#32;&#32;" ); form.append( submitButton.$element ); form.append( cancelButton.$element ); form.append( nextButton.$element ); form.append ( $( "" )				.attr( "id", "draft-sorter-status" )			); form.show; $( select ) .val( existingProjects ) .chosen( {"placeholder_text_multiple": "Select some WikiProjects"} ) .on("change", function(evt, params) { //Make existing projects undeletable					$( "#draft-sorter-status" ).empty;					if ( predicts.length > 0 ) { showPredicts; }					if ( params.deselected && existingProjects.includes(params.deselected) ) {						$( select ).val( $( select ).val.concat([params.deselected]) ).trigger("chosen:updated");						$( "#draft-sorter-status" ).prepend( $( "" ) .text( "Draft Sorter cannot remove existing WikiProjects." ) .addClass( "error" ) );					}				} );

// Add completed form to the page $( '#draft-sorter-wrapper' ).replaceWith(form); getPredicts; return; }

// The submission function function submit { $( "#draft-sorter-form" ) .attr("disabled", true) .trigger("chosen:updated"); submitButton .setLabel( "Submitting..." ) .setDisabled( true ); cancelButton .setLabel ( "Close" ); var newTags = [];

$( "#draft-sorter-form" ).val.forEach( function (element) {				if ( !existingProjects.includes(element) ) {					newTags.push(element);				}			} );

console.log( newTags.length + " new tag(s): " + newTags.join(", ") ); var statusList = $( "#draft-sorter-status" ) .html( "Saving " + newTags.length + " new tags." ); var showStatus = function ( status ) { return $( "" ) .text( status ) .appendTo( statusList ); };			var newText = ""; newTags.forEach( function ( element ) {					newText += "\n";			} );

function editTalk(text, prefix) { var params = { action: "edit", title: "Draft talk:" + mw.config.get( "wgTitle" ), summary: "Tagging draft: +" + newTags.join(", +") + " (draft-sorter)", };				params[prefix + "text"] = text;

new mw.Api.postWithEditToken( params ).done( function ( data ) {					if ( data && data.edit && data.edit.result && data.edit.result === "Success" ) {						showStatus( "Edit saved successfully! (" ) .append( $( "" )								.text( "reload" )								.attr( "href", "#" )								.click( function { window.location.replace( 										window.location.href.replace("draftsorttrigger=y","")									); } )							).append( ")" );						submitButton.setLabel( "Submitted" );						nextButton.setLabel( "Next draft" ).setFlags( [ 'progressive' ] );					} else {						showStatus( "Couldn't save due to error: " + JSON.stringify( data ) );					}				} ).fail( function ( error ) {					showStatus( "Couldn't save due to error: " + JSON.stringify( error ) );				} ); return; }

new mw.Api.get( {				action: "query",				titles: "Draft talk:" + mw.config.get( 'wgTitle' ),				prop: "templates",				tltemplates: "Template:WikiProject_banner_shell"			} ).done (function (data) {				var bannerShellUsed = Object.entries(data.query.pages)[0][1].templates;				if(typeof(bannerShellUsed) == "object" && bannerShellUsed.length > 0) {					api.get( { action: "parse", page: "Draft talk:" + mw.config.get( 'wgTitle' ), prop: "wikitext", section: "0" } ).done (function (data) { var talkText = data.parse.wikitext["*"]; if (typeof(talkText) == "string") { var pattern = /(\{\{\s*(?:Wiki[ _]?Project[ _]?banners?[ _]?shell(?:\/redirect)?|(?:(?:WP)?[ _]?Banner|(?:Wiki)?Project|Scope)[ _]?shell|Multiple[ _]wikiprojects|WikiProject[ _]?Banners?|WPBS?)\s*\|\s*)/im; if (talkText.search(pattern) >= 0) { newText = talkText.replace( pattern, ("$1" + newText) ); editTalk(newText,""); } else { console.log("Banner shell on talk page, but not found in wikitext: " + talkText); editTalk(newText,"prepend"); }						} else { console.log("typeof(talkText) = " + typeof(talkText)); editTalk(newText,"prepend"); }					} ).fail (function (error) { console.warn( "Couldn't retrieve talk page text due to error: " + JSON.stringify( error ) ); editTalk(newText,"prepend"); } );				} else if(newTags.length > 2) {					console.log("typeof(bannerShellUsed) = " + typeof(bannerShellUsed) );					newText = "";					editTalk(newText,"prepend");				} else {					console.log("typeof(bannerShellUsed) = " + typeof(bannerShellUsed) + "; newTags.length = " + newTags.length);					editTalk(newText,"prepend");				}			} ).fail( function ( error ) {				console.warn( "Couldn't retrieve templates on talk page due to error: " + JSON.stringify( error ) );				editTalk(newText,"prepend");			} ); return; }	} );	if (mw.util.getParamValue('draftsorttrigger')) {		$( portletLink ).trigger("click");	} } ) }( jQuery, mediaWiki ) ); //