User:Ahecht/Scripts/draft-sorter.js: Difference between revisions

Content deleted Content added
read external includelist and blocklist
var api = new mw.Api( { userAgent: 'draft-sorter/0.0.1' } );
 
(14 intermediate revisions by the same user not shown)
Line 1:
//jshint maxerr:512
//jshint esnext:false
//jshint esversion:8
 
//Based on [[User:Enterprisey/draft-sorter.js]] <nowiki>
( function ( $, mw ) { mw.loader.using( ["mediawiki.api", "jquery.chosen", "oojs-ui-core"], function () {
mw.loader.load( "mediawiki.ui.input", "text/css" );
var api = new mw.Api( { userAgent: 'draft-sorter/0.0.1' } );
 
if ( mw.config.get( "wgNamespaceNumber" ) !== 118 ) {
Line 22 ⟶ 26:
// 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";
var templateIncludelistName = "Wikipedia:WikiProject Articles for creation/WikiProject templates.json/includelist.json";
var templateBlocklistName = "Wikipedia:WikiProject Articles for creation/WikiProject templates.json/blocklist.json";
var wikiProjectMainCategory = "Category:WikiProject banner wrapper templates";
var wikiProjectCategories = [ // other WikiProject categories not included in wikiProjectMainCategory
"Category:WikiProject banners with quality assessment",
"Category:WikiProject banners without quality assessment",
"Category:WikiProject banner templates not based on WPBannerMeta",
"Category:Inactive WikiProject banners",
"Category:WikiProject banner wrapper templates",
];
// Define the form
Line 53 ⟶ 47:
);
// Add the form to the page
form.insertAfter( "#jump-to-navcontentSub" );
 
var select = $( "<select>" )
Line 78 ⟶ 72:
 
// Determine what templates are already on the talk page
var api = new mw.Api();
var existingProjects = [];
var wikiprojects = {};
var templateBlockList = fetchJSONList(templateBlocklistName);
 
api.get( {
Line 100 ⟶ 92:
console.log( "Project templates found on talk page: ");
console.log( existingProjects );
fetchJSONList(templateCache).then( (cachedList) => {
checkTemplateCache();
wikiprojects = cachedList;
constructForm();
} );
} ).fail (function() {
console.log("Retrieving project templates from talk page failed.");
fetchJSONList(templateCache).then( (cachedList) => {
checkTemplateCache();
wikiprojects = cachedList;
constructForm();
} );
});
predicts = [];
async function fetchJSONList(listName) {
var parsedList = {}, listData;
var listDataquery = JSON.parse($.ajax({
action:'parse',
url: mw.util.wikiScript('api'), async:false,
prop:'wikitext',
error: function (jsondata) {
page: listName,
console.log("Unable to fetch contents of " + listName + ": " + jasondata);
formatversion: '2',
return {};
origin: '*'
}, data: {action:'parse', format:'json', prop:'wikitext', page: listName, formatversion: '2' }
};
}).responseText).parse;
try {
if (listData && listData.wikitext) {
listData = await 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.logwarn("Error parsing JSON list " + listName + ": " + jsonerror);
console.log(jsonerror);
}
} else {
console.log("Error parsing " + listName + " data:");
console.log(listData);
}
 
return parsedList;
}
function checkTemplateCache() {
wikiprojects = fetchJSONList(templateCache);
 
if (wikiprojects._timestamp) {
var cacheTimestamp = new Date(wikiprojects._timestamp);
delete wikiprojects._timestamp;
//Check if cache timestamp is more than 24 hours old
if ( isFinite(cacheTimestamp) && (Date.now() - cacheTimestamp < 86400000) ) {
console.log("Cache timestamp \"" + cacheTimestamp + "\" OK.");
} else {
console.log("Cache timestamp \"" + cacheTimestamp + "\" too old. Discarding.");
wikiprojects = {};
}
} else {
console.log("Cannot find timestamp in project cache list. Discarding.");
wikiprojects = {};
}
 
if (Object.entries(wikiprojects).length == 0) {
console.log("Cache empty. Fetching new project list...");
getTemplateCategories();
} else {
console.log("Using cached project list.");
constructForm();
}
}
function writeTemplateCache() {
wikiprojects = Object.keys(wikiprojects).sort().reduce(
(obj, key) => {
obj[key] = wikiprojects[key];
return obj;
},
{}
);
wikiprojects._timestamp = new Date().toJSON();
new mw.Api().postWithToken("csrf", { action: 'edit',
title: templateCache,
text: JSON.stringify(wikiprojects),
summary: "Update WikiProject cache ([[User:Ahecht/Scripts/draft-sorter|draft-sorter]])",
watchlist:"unwatch"
} ).done( function(reslt) {
constructForm();
} ).fail( function(reslt) {
console.log("Error updating cache: " + reslt);
constructForm();
} );
}
function nextDraft() {
// Special:RandomInCategory isn't random, souse thistoolforge function is ainstead
// better substitute.
draftList = [];
console.log ("Fetching drafts from API");
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");
getDrafts();
function getDrafts(cont) { // Recursively call API
var api = new mw.Api();
var query = {
action: "query",
list: "categorymembers",
cmtitle: "Category:Pending_AfC_submissions",
cmprop: "title",
cmnamespace: "118",
cmtype: "page",
cmlimit: "max"
};
if (cont) {
query = Object.assign(query, cont);
}
api.get( query )
.done (function (data) {
if (data && data.query && data.query.categorymembers) { //API query returned pages
data.query.categorymembers.forEach(function(item) {
draftList.push( item.title );
} );
}
if (data && data.continue) { //More results are available
getDrafts(data.continue);
} else { // Redirect to random page
console.log("Done fetching drafts!");
window.___location.href = mw.config.get( "wgServer" )
+ "/wiki/"
+ draftList[Math.random() * draftList.length | 0]
+ "?draftsorttrigger=y";
}
} ).fail (function(error) { // Use Special:RandomInCategory
console.log("Error getting list of drafts: " + error);
window.___location.href = mw.config.get( "wgServer" )
+ "/wiki/Special:RandomInCategory/Pending_AfC_submissions?draftsorttrigger=y";
} );
return;
}
}
Line 236 ⟶ 147:
$( "#draft-sorter-status" ).append( "<li>Suggested categories from <a href=\"https://www.mediawiki.org/wiki/ORES#Topic_routing\">ORES</a>:<ul id=\"draft-sorter-suggest\"></ul></li>" );
predicts.forEach( function(item) {
function addWithLink(p) {
var addLink = $( "<a>" )
$( "#draft-sorter-suggest" ).append(
.text("add")
$( "<li>" ).clicktext( functionitem + " ()" {).append(
$( select"<a>" ).valtext("add").click(
function() {
$( select ).val().concat( [ "WikiProject " + item ] )
$( select ).val(
).trigger("chosen:updated");
$( 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);
$( "#draft-sorter-suggest" ).append( $( "<li>" )
.append( item + " (" )
.append( addLink )
.append( ")" )
);
} else if( singularItem != item
&& !existingProjects.includes( "WikiProject " + singularItem )
&& wikiprojects[singularItem]
) { //Singular form of prediction matches a WikiProject and doesn't exist
addWithLink(singularItem);
addLink.click( function() {
$( select ).val(
$( select ).val().concat( [ "WikiProject " + singularItem ] )
).trigger("chosen:updated");
} );
$( "#draft-sorter-suggest" ).append( $( "<li>" )
.append( singularItem + " (" )
.append( addLink )
.append( ")" )
);
} else { //Prediction doesn't match a WikiProject or already exists
$( "#draft-sorter-suggest" ).append(
Line 276 ⟶ 181:
function getPredicts() {
var dbNamelang = mw.config.get( "wgDBnamewgServerName").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({
$.getJSON( "//ores.wikimedia.org/v3/scores/" + dbName + "/" + revID + "/drafttopic" )
"rev_id": revID,
.done( function ( data ) {
"lang": lang[0],
if(data && data[dbName] && data[dbName].scores &&
"page_title": mw.config.get("wgPageName")
data[dbName].scores[revID] &&
});
data[dbName].scores[revID].drafttopic &&
data[dbName].scores[revID].drafttopic.score &&
data[dbName].scores[revID].drafttopic.score.prediction) {
var prediction = data[dbName].scores[revID].drafttopic.score.prediction;
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.log("Error finding predictions in ORES response:");
console.log(data);
}
} ).fail( function ( error ) {
console.log("Error retrieving ORES data: " + error);
} );
return;
}
 
fetch(liftWingExternalEndpoint + model + ":predict", {
function getTemplateCategories(cont) { // Recursively call API
method: "POST",
var api = new mw.Api();
headers: new Headers({
var query = {
action "Content-Type": "queryapplication/json",
"User-Agent": "draft-sorter (https://en.wikipedia.org/wiki/User:Ahecht/Scripts/draft-sorter.js)"
list: "categorymembers",
}),
cmtitle: wikiProjectMainCategory,
cmtypebody: "subcat",postBody
}).then(response => response.json()).then(data => {
cmlimit: "max"
var prediction = [];
};
var dbName = mw.config.get("wgDBname");
if (cont) {
query = Object.assign(query, cont);
}
api.get( query )
.done (function (data) {
if (data && data.query && data.query.categorymembers) { //API query returned members
Object.entries(data.query.categorymembers).forEach( function(item) {
wikiProjectCategories.push(item[1].title);
} );
}
if (data && data.continue) { //More results are available
getTemplateCategories(data.continue);
} else {
//console.log(wikiProjectCategories);
getTemplatesFromCategories();
}
} ).fail (function(error) {
console.log("Error getting list of categories: " + error);
} );
return;
}
function getTemplatesFromCategories(catTitle, cont) { // Recursively call API
if (typeof catTitle === 'undefined') { // Grab next item in wikiProjectCategories
catTitle = wikiProjectCategories.pop();
}
if (typeof catTitle === 'undefined') { // No remaining entries in wikiProjectCategories
// Manually add Wikiprojects with /s in their title
jQuery.extend( wikiprojects, fetchJSONList(templateIncludelistName) );
writeTemplateCache();
} else {
if(data && data[dbName] && data[dbName].scores &&
var api = new mw.Api();
data[dbName].scores[revID] &&
var query = {
data[dbName].scores[revID].drafttopic &&
action: "query",
data[dbName].scores[revID].drafttopic.score &&
list: "categorymembers",
data[dbName].scores[revID].drafttopic.score.prediction) {
cmtitle: catTitle,
prediction = data[dbName].scores[revID].drafttopic.score.prediction;
cmtype: "page",
} else if (data && data.prediction && data.prediction.results) {
cmnamespace: "10",
data.prediction.results.forEach( p => {
cmlimit: "max"
if (p && p.topic) prediction.push(p.topic);
};
if } (cont) {;
}
query = Object.assign(query, cont);
}
if (prediction.length) {
api.get( query )
console.log("Got ORES response! Raw predictions:");
.done (function (data) {
console.log(prediction);
if (data && data.query && data.query.categorymembers) { //API query returned members
Object.entries(data.query.categorymembers).forEach( function(item) {
prediction.forEach( function (item) {
var title = item[1].title.match(/^Template:WikiProject\s(.*)$/);
var last = item.split(".")[item.split(".").length-1];
if (title && title[1] && title[1] !== "") { //Valid page name format
var penultimate = item.split(".")[item.split(".").length-2];
if (title[1].indexOf("/") == -1 || title[1].match(/ task ?force$/i) ) { //No subpages
title[1]if =( title[1]last.replacesubstr(/-1) task== ?force$/i,"*" ); {
// Filter out redundant starred predictions
if(templateBlockList[ title[1] ]) {
if (prediction.find(element => (
//console.log("Entry on blocklist:" + title[1]);
element.split(".")[element.split(".").length-1] != last &&
} else {
element.split(".")[element.split(".").length-2] == penultimate
wikiprojects[ title[1] ] = item[1].title.replace("Template:", "");
}) ) ) {
console.log("Prediction \"" + last + "\" excluded.");
} else {
last = null;
//console.log("Subpage rejected:" + item[1].title);
} else {
}last else= {penultimate;
}
//console.log("No valid title found in the following item:" + item[1].title);
}
} );
}
if (data && data.continue) { //More results are available
if ( wikiprojects[last] ) {
getTemplatesFromCategories(catTitle, data.continue);
// WikiProject found, no need to try splitting
} else {
getTemplatesFromCategoriespredicts.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] );
}
}
} ).fail (function(error) {
console.log("Error getting list of templates: " + error);
constructForm();
} );
console.log("Filtered predictions:");
return;
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;
}
 
Line 426 ⟶ 269:
mw.loader.load( "oojs-ui.styles.icons-movement");
console.log( "WikiProjects: ");
console.log( wikiprojects );
console.log( "Arguments: ");
console.log( arguments );
Object.keys(wikiprojects).sort().forEach( function(name) {
select.append( $( "<option>" )
Line 508 ⟶ 347:
function editTalk(text, prefix) {
var params = {
action: "edit", section: "0",
title: "Draft talk:" + mw.config.get( "wgTitle" ),
summary: "Tagging draft: +" + newTags.join(", +") +
" ([[User:Ahecht/Scripts/draft-sorter.js|draft-sorter.js]])",
};
params[prefix + "text"] = text;
Line 554 ⟶ 393:
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*[a-z1]+\s*=[^\{\}]*)*\s*(?:\\n)*?)/im;
if (talkText.search(pattern) >= 0) {
newText = talkText.replace( pattern, ("$1" + newText) );
Line 567 ⟶ 406:
}
} ).fail (function (error) {
console.logwarn( "Couldn't retrieve talk page text due to error: " + JSON.stringify( error ) );
editTalk(newText,"prepend");
} );
Line 579 ⟶ 418:
}
} ).fail( function ( error ) {
console.logwarn( "Couldn't retrieve templates on talk page due to error: " + JSON.stringify( error ) );
editTalk(newText,"prepend");
} );