User:JSherman (WMF)/revertrisk.js
Appearance
Code that you insert on this page could contain malicious content capable of compromising your account. If you import a script from another page with "importScript", "mw.loader.load", "iusc", or "lusc", take note that this causes you to dynamically load a remote script, which could be changed by others. Editors are responsible for all edits and actions they perform, including by scripts. User scripts are not centrally supported and may malfunction or become inoperable due to software changes. A guide to help you find broken scripts is available. If you are unsure whether code you are adding to this page is safe, you can ask at the appropriate village pump. This code will be executed when previewing this page. |
Documentation for this user script can be added at User:JSherman (WMF)/revertrisk. |
// <nowiki>
/*
* Add a portlet link on diff pages that:
* - fetches the language agnostic revert risk score
* - displays it in a Codex dialog
* See: https://meta.wikimedia.org/wiki/Machine_learning_models/Proposed/Language-agnostic_revert_risk
* @author: JSherman (WMF)
*/
( function () {
// Define the Vue component in a variable because this is a single file
const rootComponent = {
name: 'DiffRevertRisk',
// Wrap the Vue template in a js template string because this is a
// JavaScript (.js) file, not a Vue Single File Component (.vue)
// See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals
template: `
<cdx-dialog
v-model:open="open"
title="Revert Risk"
close-button-label="Close"
:default-action="{label: 'Close'}"
@default="open = false"
>
<p>
<h3>Revert probability</h3> {{ data }}
<h3>Revision</h3> {{ revId }}
<h4><cdx-icon :icon="cdxIconInfo" />Revert risk technical info</h4>
<ul><li>
<a href="https://meta.wikimedia.org/wiki/Machine_learning_models/Proposed/Language-agnostic_revert_risk" target="_blank" rel="noopener noreferrer">Language-agnostic revert risk model card</a>
</li><li>
<a href="https://api.wikimedia.org/wiki/Lift_Wing_API/Reference/Revert_risk_score_object" target="_blank" rel="noopener noreferrer">Language-agnostic revert score object</a>
</li></ul>
</p>
</cdx-dialog>
`,
// Run in Vue 3 mode
// See: https://www.mediawiki.org/wiki/Vue.js/Vue_3_migration
compatConfig: {
MODE: 3
},
compilerOptions: {
whitespace: 'condense'
},
props: {
revId: { type: Number, required: true },
lang: { type: String, required: true }
},
data: function () {
return { open: false, data: null };
},
methods: {
// Fetch the language-agnostic revert risk for this revision
fetchData: function () {
fetch( 'https://api.wikimedia.org/service/lw/inference/v1/models/revertrisk-language-agnostic:predict', {
method: 'POST',
// eslint-disable-next-line camelcase
body: JSON.stringify( { rev_id: this.revId, lang: this.lang } )
} )
.then( function ( response ) {
return response.json();
} )
.then( function ( inferenceData ) {
try {
// Get score if it exists
const score = ( ( ( ( inferenceData || {} ).output || {} ).probabilities || {} ).true );
// If there is a score, display it to 2 decimal points
// Otherwise display the 'detail' or 'error' response element
this.data = score ? score.toFixed( 2 ) : ( ( ( inferenceData || {} ).detail || inferenceData || {} ).error );
} catch ( error ) {
this.data = error;
}
}.bind( this ) );
}
},
mounted: function () {
// Get the toolbox
const toolboxId = 'p-tb',
toolbox = document.getElementById( toolboxId );
if ( !toolbox ) {
return;
}
// Create the portlet link
const portletLink = mw.util.addPortletLink(
toolboxId,
'#',
'Get revert risk',
'user-jsherman-wmf-revertrisk-fetch'
);
// Open the dialog and fetch the data (if necessary) on click
portletLink.addEventListener( 'click', function ( e ) {
// Prevent scrolling that would normally occur with '#' anchor
e.preventDefault();
this.open = true;
// return early if we already have data for this revision
if ( this.data ) {
return;
}
this.fetchData();
}.bind( this ) );
},
setup: function () {
// A hacky way to use codex icons in user scripts is to just copy the svg for the icon
// See: https://doc.wikimedia.org/codex/latest/icons/all-icons.html
const cdxIconInfo = '<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="20" height="20" viewBox="0 0 20 20" aria-hidden="true"><!----><g><path d="M4 10a6 6 0 1012 0 6 6 0 00-12 0m6-8a8 8 0 110 16 8 8 0 010-16m1 7v5H9V9zm0-1V6H9v2z"></path></g></svg>';
return { cdxIconInfo };
}
};
// The list of supported projects as of 2023-10-18
function validLang( lang ) {
if ( !lang ) {
return false;
}
return [ 'aa', 'ab', 'ace', 'ady', 'af', 'ak', 'als', 'alt', 'am', 'ami', 'an', 'ang', 'anp', 'ar', 'arc', 'ary', 'arz', 'as', 'ast', 'atj', 'av', 'avk', 'awa', 'ay', 'az', 'azb', 'ba', 'ban', 'bar', 'bat-smg', 'bcl', 'be', 'be-tarask', 'be-x-old', 'bg', 'bh', 'bi', 'bjn', 'blk', 'bm', 'bn', 'bo', 'bpy', 'br', 'bs', 'bug', 'bxr', 'ca', 'cbk-zam', 'cdo', 'ce', 'ceb', 'ch', 'cho', 'chr', 'chy', 'ckb', 'co', 'cr', 'crh', 'cs', 'csb', 'cu', 'cv', 'cy', 'da', 'dag', 'de', 'din', 'diq', 'dsb', 'dty', 'dv', 'dz', 'ee', 'el', 'eml', 'en', 'eo', 'es', 'et', 'eu', 'ext', 'fa', 'fat', 'ff', 'fi', 'fiu-vro', 'fj', 'fo', 'fr', 'frp', 'frr', 'fur', 'fy', 'ga', 'gag', 'gan', 'gcr', 'gd', 'gl', 'glk', 'gn', 'gom', 'gor', 'got', 'gpe', 'gsw', 'gu', 'guc', 'gur', 'guw', 'gv', 'ha', 'hak', 'haw', 'he', 'hi', 'hif', 'ho', 'hr', 'hsb', 'ht', 'hu', 'hy', 'hyw', 'hz', 'ia', 'id', 'ie', 'ig', 'ii', 'ik', 'ilo', 'inh', 'io', 'is', 'it', 'iu', 'ja', 'jam', 'jbo', 'jv', 'ka', 'kaa', 'kab', 'kbd', 'kbp', 'kcg', 'kg', 'ki', 'kj', 'kk', 'kl', 'km', 'kn', 'ko', 'koi', 'kr', 'krc', 'ks', 'ksh', 'ku', 'kv', 'kw', 'ky', 'la', 'lad', 'lb', 'lbe', 'lez', 'lfn', 'lg', 'li', 'lij', 'lld', 'lmo', 'ln', 'lo', 'lrc', 'lt', 'ltg', 'lv', 'lzh', 'mad', 'mai', 'map-bms', 'mdf', 'mg', 'mh', 'mhr', 'mi', 'min', 'mk', 'ml', 'mn', 'mni', 'mnw', 'mr', 'mrj', 'ms', 'mt', 'mus', 'mwl', 'my', 'myv', 'mzn', 'na', 'nah', 'nan', 'nap', 'nds', 'nds-nl', 'ne', 'new', 'ng', 'nia', 'nl', 'nn', 'no', 'nostalgia', 'nov', 'nqo', 'nrm', 'nso', 'nv', 'ny', 'oc', 'olo', 'om', 'or', 'os', 'pa', 'pag', 'pam', 'pap', 'pcd', 'pcm', 'pdc', 'pfl', 'pi', 'pih', 'pl', 'pms', 'pnb', 'pnt', 'ps', 'pt', 'pwn', 'qu', 'rm', 'rmy', 'rn', 'ro', 'roa-rup', 'roa-tara', 'ru', 'rue', 'rup', 'rw', 'sa', 'sah', 'sat', 'sc', 'scn', 'sco', 'sd', 'se', 'sg', 'sgs', 'sh', 'shi', 'shn', 'si', 'simple', 'sk', 'skr', 'sl', 'sm', 'smn', 'sn', 'so', 'sq', 'sr', 'srn', 'ss', 'st', 'stq', 'su', 'sv', 'sw', 'szl', 'szy', 'ta', 'tay', 'tcy', 'te', 'test', 'test2', 'tet', 'tg', 'th', 'ti', 'tk', 'tl', 'tly', 'tn', 'to', 'tpi', 'tr', 'trv', 'ts', 'tt', 'tum', 'tw', 'ty', 'tyv', 'udm', 'ug', 'uk', 'ur', 'uz', 've', 'vec', 'vep', 'vi', 'vls', 'vo', 'vro', 'wa', 'war', 'wo', 'wuu', 'xal', 'xh', 'xmf', 'yi', 'yo', 'yue', 'za', 'zea', 'zh', 'zh-classical', 'zh-min-nan', 'zh-yue', 'zu' ].indexOf( lang ) !== -1;
}
function init() {
// Get the diff revision
const revId = parseInt( mw.util.getParamValue( 'oldid' ) );
if ( !revId || Number.isNaN( revId ) ) {
return;
}
// Get the page content
const content = document.getElementById( 'mw-content-text' );
if ( !content ) {
return;
}
// Get the wiki servername language code
const lang = mw.config.get( 'wgServerName' ).split( '.' )[ 0 ];
if ( !validLang( lang ) ) {
return;
}
// Insert a container in which we'll mount the app
const container = document.createElement( 'div' );
container.setAttribute( 'id', '#user-jsherman-wmf-revertrisk-dialog' );
content.insertBefore( container, content.firstChild );
mw.loader.using(
[
'vue',
'@wikimedia/codex'
],
// Register codex components and mount App
function () {
const { CdxButton, CdxDialog, CdxIcon } = mw.loader.require( '@wikimedia/codex' );
const { createMwApp } = mw.loader.require( 'vue' );
createMwApp( rootComponent, { revId, lang } )
.component( 'CdxButton', CdxButton )
.component( 'CdxDialog', CdxDialog )
.component( 'CdxIcon', CdxIcon )
.mount( container );
}
);
}
mw.hook( 'wikipage.content' ).add( init );
}() );
// </nowiki>