User:Frettie/consistency check add.js
Jump to navigation
Jump to search
Note: After publishing, you may have to bypass your browser's cache to see the changes.
- Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
- Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
- Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5.
if ( typeof ( $ ) === 'undefined' ) $ = jQuery;
var consistency_check = {
_entity: null,
_refresh: false,
running: false,
reciproke: {
'P26': ['P26'], // spouse
'P451': ['P451'], // partner
'P40': ['P22', 'P25'], // mother, father
'P3373': ['P3373'], // brother and sister
'P301': ['P910'], // main article
'P1629': ['P1687'], // subject item of this property / Wikidata property
'P2875': ['P1659'], // property usage tracking category / see also
'P1753': ['P1754'], // related list
'P361': ['P527'], // parts
'P461': ['P461'], // opposite of
'P460': ['P460'], // said to be same as
'P1889': ['P1889'], // different from
'P2959': ['P2959'], // permanent duplicated
'P710': ['P1344'], // participant (of)
'P197': ['P197'], // adjacent station
'P2789': ['P2789'], // connects with
'P403': ['P974'], // mouth of the watercourse / tributary
'P155': ['P156'], // previous / next
'P1365': ['P1366'], // replaced by /replaces
'P167': ['P1398'], // structure replaced by / replaces
'P47': ['P47'], // shares border with
'P190': ['P190'], // twinned administrative body
'P36': ['P1376'], // capital (of)
'P530': ['P530'], // diplomatic relation
'P828': ['P1542'], // cause
'P802': ['P1066'],
'P749': ['P355'],
'P747': ['P629'],
'P674': ['P1441'], // charaters and present in work
'P1830': ['P127'], // owner of/owned by
'P1382': ['P1382'], // partially coincident with
'P1560': ['P1560'], // given name version for other gender
'P2743': ['P2743'], // this zoological name is coordinate with
'P184': ['P185'], // doctoral advisor
'P2674': ['P2673'], // next crossing downstream
'P3032': ['P3032'], // adjacent building
'P1423': ['P1424'], // template has topic / topic's main template
'P2388': ['P2389'], // organization directed by the office or position
'P4969': ['P144'], // opera derivata (P4969) <-> basato su (P144)
},
init: function ( entity ) {
var self = this;
if ( ( mw.config.get( 'wgNamespaceNumber' ) !== 0 && mw.config.get( 'wgNamespaceNumber' ) !== 120 ) ||
!mw.config.exists( 'wbEntityId' ) ||
!mw.config.get( 'wbIsEditView' )
) {
return;
}
self._entity = entity;
self.entityId = mw.config.get( 'wbEntityId' );
if(!wb || !wb.api || !wb.api.RepoApi) return; // TypeError: Cannot read property 'RepoApi' of undefined (e.g. mobile site)
self.repoApi = new wb.api.RepoApi( new mw.Api() );
$.each( self.reciproke, function ( key, props ) {
props.forEach( function ( prop ) {
if ( undefined === self.reciproke[prop] ) self.reciproke[prop] = [];
if ( self.reciproke[prop].indexOf( key ) === -1 ) self.reciproke[prop].push( key );
} );
} );
mw.hook( 'wikibase.statement.saved' ).add( function ( _, statement ) {
self._entity = null;
self._refresh = true;
// FIXME: buggy, disabled per talk page and T252078
// self.reciprocityAdder( statement );
} );
var portletLink = mw.util.addPortletLink( 'p-tb', '#', 'Consistency', 't-consistency_check' );
$( portletLink ).on( 'click', function ( e ) {
e.preventDefault();
self.run();
return false;
} );
},
getEntity: function () {
var self = this;
if ( self._entity !== null ) {
return $.Deferred().resolve( self._entity );
} else {
return self.repoApi.getEntities( [ self.entityId ] )
.then( function ( data ) {
self._entity = data.entities[ self.entityId ];
return self._entity;
} );
}
},
run: function () {
var self = this;
if ( self.running ) return;
self.running = true;
var consistency_section = $( '#consistency-section' );
if ( consistency_section.length !== 0 && self._refresh !== true ) {
self.running = false;
return;
}
consistency_section.remove();
self._refresh = false;
var langs = mw.language.getFallbackLanguageChain();
self.getEntity()
.then( function ( entity ) {
var check = {},
props = [],
propLabels = {};
$.each( entity.claims || {}, function ( property, claims ) {
if ( undefined === self.reciproke[ property ] ) return; // No need to check
var load_property = false;
claims.forEach( function ( claim ) {
if ( !claim.mainsnak.datavalue ) return;
const q = claim.mainsnak.datavalue.value.id;
if ( check[ q ] === undefined ) {
check[ q ] = [];
}
check[ q ].push( {
id: 'consistency_' + property + '_' + q,
prop: property,
} );
load_property = true;
} );
if ( load_property ) {
props.push( property );
}
} );
var promise;
if ( props.length > 0 ) {
promise = self.repoApi.getEntities(
props,
[ 'labels' ],
langs
).then( function( data ) {
props.forEach( function ( p ) {
propLabels[ p ] = p;
var labels = data.entities[ p ].labels;
$.each( langs, function ( _, lang ) {
if ( labels[ lang ] !== undefined ) {
propLabels[ p ] = labels[ lang ].value;
return false;
}
} );
} );
var table = '<div><table border=1>';
$.each( check, function ( q, objs ) {
objs.forEach( function ( d ) {
table += '<tr><th class="label_' + d.prop + '">' + propLabels[ d.prop ] + '</th>';
table += '<td id="' + d.id + '_c" style="text-align:center">?</td>';
table += '<td id="' + d.id + '_v"><a id="' + d.id + '_vh" href="/entity/' + q + '">' + q + '</a></td></tr>';
} );
} );
table += "</table></div>";
return table;
} );
} else {
promise = $.Deferred().resolve( '<div>No claims found for ' + self.entityId + '.</div>' ).promise();
}
return promise.then( function ( table ) {
const header = '<h2 class="wb-section-heading" id="consistency-heading">Consistency</h2>';
$( '<div>' )
.attr( 'id', 'consistency-section' )
.addClass( 'consistency' )
.html( header + table )
.insertBefore( $( 'h2.wb-section-heading' ).get( 0 ) );
if ( check.length === 0 ) {
return;
}
const
oneway = '<span class="oneway" style="color:red; font-weight:bold">→</span>',
bothways = '<span style="color:green">↔</span>',
append = ' <span class="append" style="cursor:pointer; color:red; font-weight:bold">+</span>';
var ids = Object.keys( check ),
idgroups = [];
const
length = ids.length,
chunk = 40;
for (var i = 0; i < length; i += chunk) {
idgroups.push(ids.slice(i, i+chunk));
}
return $.when.apply( $, idgroups.map( function ( group ) {
return self.repoApi.getEntities(
group,
[ 'claims', 'info', 'labels' ],
langs
)
.then( function ( data ) {
$.each( data.entities, function ( id, entity ) {
var labelTo = '';
$.each( langs, function ( _, lang ) {
if ( entity.labels[lang] !== undefined ) {
labelTo = entity.labels[lang].value;
return false;
}
} );
check[ id ].forEach( function ( d ) {
var found = false,
has = [];
$.each( self.reciproke[ d.prop ], function ( _, prop ) {
$.each( entity.claims[ prop ] || [], function ( _, value ) {
if ( !value.mainsnak.datavalue ) return;
if ( has.indexOf( prop ) === -1 ) {
has.push( prop );
}
const q = value.mainsnak.datavalue.value.id;
found = ( q === self.entityId );
return !found;
} );
return !found;
} );
if ( labelTo !== '' ) {
$( '#' + d.id + '_vh' ).text( labelTo );
}
var $click = $( '#' + d.id + '_c' );
if ( found ) {
$click.html( bothways );
} else if (
( self.reciproke[ d.prop ].length === 1 ) ||
( self.reciproke[ d.prop ].length - has.length === 1 )
) {
var prop;
$.each( self.reciproke[ d.prop ], function ( _, _prop ) {
prop = _prop;
return has.indexOf( _prop ) !== -1;
} );
$click
.html( oneway + append )
.children( 'span.append' )
.data( 'prop', prop )
.data( 'target', id )
.data( 'lastrevid', entity.lastrevid )
.on( 'click', function ( event ) {
event.preventDefault();
self.addStatement( this );
} );
}
} );
} );
} );
} ) );
} )
.then( function () {
self.running = false;
} );
} );
},
createClaim: function ( entityId, baseRevId, snakType, propertyId, value ) {
var self = this;
if (
typeof entityId !== 'string'
|| typeof baseRevId !== 'number'
|| typeof snakType !== 'string'
|| typeof propertyId !== 'string'
|| value && typeof value !== 'string' && typeof value !== 'object'
) {
throw new Error( 'Parameter not specified properly' );
}
var params = {
action: 'wbcreateclaim',
entity: entityId,
baserevid: baseRevId,
snaktype: snakType,
property: propertyId,
summary: 'using [[User:Frettie/consistency check add.js]]',
};
if ( value ) {
params.value = JSON.stringify( value );
}
return self.repoApi.post( params );
},
addStatement: function ( el ) {
var $this = $( el ),
self = this;
self.createClaim(
$this.data( 'target' ),
$this.data( 'lastrevid' ),
'value',
$this.data( 'prop' ),
{ id: self.entityId }
)
.then( function ( data ) {
var color;
if ( data.success ) {
$this
.html( '↔' )
.css( 'color', 'green' )
.siblings( 'span' ).remove();
color = '#baffc9';
} else {
color = '#ffb3ba';
}
$this.parent().parent().css( 'background-color', color );
} );
},
reciprocityAdder: function ( statement ) {
var self = this;
self.repoApi._api.get( {
action: 'wbgetclaims',
claim: statement
} )
.done( function ( data ) {
var target = null, prop = null;
$.each( data.claims, function ( _prop, value ) {
prop = _prop;
if ( value[0].mainsnak.datavalue ) {
target = value[0].mainsnak.datavalue.value.id;
}
return false;
} );
if ( !prop || !target || ( self.reciproke[ prop ] || [] ).length !== 1 ) {
return;
}
self.repoApi.getEntities(
[ target ],
[ 'claims', 'info' ]
)
.done( function ( data ) {
var found = false;
$.each( data.entities[ target ].claims[ prop ] || [], function ( _, value ) {
if ( !value.mainsnak.datavalue ) return;
const q = value.mainsnak.datavalue.value.id;
found = ( q === self.entityId );
return !found;
} );
if ( found ) {
return;
}
self.createClaim(
target,
data.entities[ target ].lastrevid,
'value',
self.reciproke[ prop ][ 0 ],
{ id: self.entityId }
)
.done( function ( data ) {
// TODO: use mw.notify?
var color = data.success ? '#baffc9' : '#ffb3ba',
selector = $.escapeSelector( statement );
$( '#' + selector ).css( 'background-color', color );
} );
} );
} );
},
the_end: ''
};
$( function () {
mw.hook( 'wikibase.entityPage.entityLoaded' ).add( function ( entity ) {
consistency_check.init( entity );
} );
} );