MediaWiki:Common.js: Difference between revisions

Content deleted Content added
No edit summary
Tag: Manual revert
No edit summary
Line 439:
}
})();
 
/* ── Datacheck confirm/correct gadget ───────────────────────────────────────
On a Datacheck: page (namespace 3006), turn each Module:ReportedTable row's
.rt-confirm control into Confirm (toggle) + Reject/Correct (enter a number),
writing the verdict straight into the underlying Data:<...>.json page under the
REVIEWER'S OWN session (no bot creds). DOM order of .rt-confirm == records[]
order (the module emits exactly one per record); the exact Data title comes
from the hidden .rt-data-title marker. Verdicts survive a re-publish via
wiki_extract.merge_verdicts (value-gated). Anonymous users see read-only marks. */
$(function () {
if (!(window.mw && mw.config) || mw.config.get('wgNamespaceNumber') !== 3006) { return; }
var marker = document.querySelector('.rt-data-title');
var spans = document.querySelectorAll('.rt-confirm');
if (!marker || !spans.length || !mw.config.get('wgUserName')) { return; }
var dataTitle = (marker.textContent || '').trim();
if (!dataTitle) { return; }
 
mw.loader.using(['mediawiki.api', 'mediawiki.notify']).done(function () {
var api = new mw.Api(), data = null, baseRevId = null;
 
function fetchAndRender() {
api.get({ action: 'query', prop: 'revisions', rvprop: 'content|ids',
rvslots: 'main', titles: dataTitle, formatversion: 2 }).done(function (res) {
var page = res.query && res.query.pages && res.query.pages[0];
var rev = page && page.revisions && page.revisions[0];
if (!rev) { mw.notify('Datacheck: data page not found — ' + dataTitle); return; }
baseRevId = rev.revid;
var content = rev.slots ? rev.slots.main.content : rev.content;
try { data = JSON.parse(content); }
catch (e) { mw.notify('Datacheck: bad JSON in ' + dataTitle); return; }
render();
}).fail(function (code) { mw.notify('Datacheck: load failed — ' + code); });
}
 
function save(okMsg) {
api.postWithEditToken({
action: 'edit', title: dataTitle, contentmodel: 'json',
text: JSON.stringify(data, null, 2),
summary: 'Datacheck: human verdict via confirm gadget', baserevid: baseRevId
}).done(function (res) {
if (res.edit && res.edit.newrevid) { baseRevId = res.edit.newrevid; }
render();
mw.notify(okMsg || 'Datacheck: saved.');
}).fail(function (code) {
if (code === 'editconflict') { mw.notify('Datacheck: edit conflict — reloading.'); fetchAndRender(); }
else { mw.notify('Datacheck: save failed — ' + code); }
});
}
 
// Clear every verdict field (keeps the JSON clean like the Python serializer), then set one.
function setStatus(rec, status, extra) {
delete rec.human_status; delete rec.confirmed_rev; delete rec.corrected_rev;
delete rec.override_reason; delete rec.alt_source;
if (status) { rec.human_status = status; for (var k in extra) { rec[k] = extra[k]; } }
}
 
function toggleConfirm(rec) {
if (rec.human_status === 'confirmed') { setStatus(rec, null); }
else { setStatus(rec, 'confirmed', { confirmed_rev: data.source_revision || null }); }
save();
}
 
function correctRow(rec) {
var hint = 'measure=' + rec.measure + ', scale=' + (rec.scale || 1) +
(rec.currency ? ', ' + rec.currency : '');
var entered = window.prompt(
'Correct value for "' + rec.label + '" [' + (rec.business_line || 'Total') + '/' +
(rec.business_unit || 'Group') + ']\n(' + hint + ' — enter the stored value):', String(rec.value));
if (entered === null) { return; }
var num = parseFloat(String(entered).replace(/[,\s]/g, ''));
if (isNaN(num)) { mw.notify('Datacheck: not a number — ' + entered); return; }
var reason = window.prompt('Reason (optional):', rec.override_reason || 'manual correction') || 'manual correction';
setStatus(rec, 'override', { value: num, corrected_rev: data.source_revision || null, override_reason: reason });
save('Datacheck: override saved (' + num + ').');
}
 
function render() {
if (!data || !data.records) { return; }
if (spans.length !== data.records.length) {
mw.notify('Datacheck: table out of sync with the data page — reload the page.'); return;
}
Array.prototype.forEach.call(spans, function (span, i) {
var rec = data.records[i], status = rec.human_status || null;
span.innerHTML = '';
var ok = document.createElement('a');
ok.href = '#'; ok.textContent = (status === 'confirmed') ? '✓' : '○';
ok.title = (status === 'confirmed') ? 'Confirmed — click to un-confirm' : 'Click to confirm';
ok.style.cssText = 'text-decoration:none;font-weight:bold;margin-right:8px;color:' +
(status === 'confirmed' ? '#137333' : '#999');
ok.onclick = function (e) { e.preventDefault(); toggleConfirm(rec); };
span.appendChild(ok);
var fix = document.createElement('a');
fix.href = '#'; fix.textContent = (status === 'override') ? ('✎ ' + rec.value) : '✎';
fix.title = 'Reject + enter the correct number';
fix.style.cssText = 'text-decoration:none;color:' + (status === 'override' ? '#b06000' : '#999');
fix.onclick = function (e) { e.preventDefault(); correctRow(rec); };
span.appendChild(fix);
if (status === 'corrected') {
var c = document.createElement('span'); c.textContent = ' (corrected)';
c.style.color = '#137333'; span.appendChild(c);
}
});
}
 
fetchAndRender();
});
});