Jump to content

User:Polygnotus/Scripts/EditsPerNamespace.js

From Wikipedia, the free encyclopedia
Note: After saving, you have to bypass your browser's cache to see the changes. Google Chrome, Firefox, Microsoft Edge and Safari: Hold down the ⇧ Shift key and click the Reload toolbar button. For details and instructions about other browsers, see Wikipedia:Bypass your cache.
// <nowiki>
$(document).ready(function() {
    // Add link to tools menu
    mw.util.addPortletLink(
        'p-tb',
        '/wiki/Special:BlankPage/UserEditsAnalysis',
        'User Edits Analysis',
        't-user-edits-analysis',
        'Analyze user edits by namespace'
    );
    
    // Customize the page when viewing it
    if (mw.config.get('wgCanonicalSpecialPageName') === 'Blankpage' && 
        mw.config.get('wgTitle') === 'BlankPage/UserEditsAnalysis') {
        
        document.title = 'User Edits Analysis';
        $('#firstHeading').text('User Edits Analysis');
        
        $('#mw-content-text').html(`
            <div style="max-width: 800px;">
                <p>Enter a username to analyze their edit distribution across namespaces.</p>
                <input type="text" id="username-input" placeholder="Username" style="width: 300px; padding: 5px;">
                <button id="analyze-btn" style="padding: 5px 15px; margin-left: 10px;">Analyze</button>
                <div id="status" style="margin-top: 10px; color: #666;"></div>
                <div id="results" style="margin-top: 20px;"></div>
                <canvas id="chart" width="400" height="400" style="display:none; margin-top: 20px;"></canvas>
            </div>
        `);
        
        $('#analyze-btn').on('click', function() {
            const username = $('#username-input').val().trim();
            if (!username) {
                $('#status').text('Please enter a username.');
                return;
            }
            analyzeUser(username);
        });
        
        $('#username-input').on('keypress', function(e) {
            if (e.which === 13) {
                $('#analyze-btn').click();
            }
        });
    }
});

async function analyzeUser(username) {
    $('#status').text('Fetching edits...');
    $('#results').empty();
    $('#chart').hide();
    
    const namespaces = {};
    let continueToken = null;
    let totalEdits = 0;
    const delay = 1000; // 1 second between requests
    
    try {
        do {
            const params = {
                action: 'query',
                list: 'usercontribs',
                ucuser: username,
                uclimit: 500,
                ucprop: 'title',
                format: 'json',
                maxlag: 5
            };
            
            if (continueToken) {
                params.uccontinue = continueToken;
            }
            
            const response = await $.ajax({
                url: mw.config.get('wgScriptPath') + '/api.php',
                data: params,
                dataType: 'json',
                headers: {
                    'Api-User-Agent': 'UserEditsAnalysis/1.0 (Wikipedia User Script)'
                }
            });
            
            if (response.error) {
                throw new Error(response.error.info);
            }
            
            const contribs = response.query.usercontribs;
            
            for (const contrib of contribs) {
                const title = contrib.title;
                const colonIndex = title.indexOf(':');
                let namespace;
                
                if (colonIndex === -1) {
                    namespace = 'Main';
                } else {
                    const potentialNs = title.substring(0, colonIndex);
                    // Check if it's a valid namespace by checking against known namespaces
                    const nsId = mw.config.get('wgNamespaceIds')[potentialNs.toLowerCase().replace(/ /g, '_')];
                    if (nsId !== undefined) {
                        namespace = potentialNs;
                    } else {
                        namespace = 'Main';
                    }
                }
                
                namespaces[namespace] = (namespaces[namespace] || 0) + 1;
                totalEdits++;
            }
            
            $('#status').text(`Fetched ${totalEdits} edits...`);
            
            continueToken = response.continue ? response.continue.uccontinue : null;
            
            if (continueToken) {
                await new Promise(resolve => setTimeout(resolve, delay));
            }
            
        } while (continueToken);
        
        if (totalEdits === 0) {
            $('#status').text('No edits found for this user.');
            return;
        }
        
        displayResults(namespaces, totalEdits, username);
        
    } catch (error) {
        $('#status').text('Error: ' + error.message);
    }
}

function displayResults(namespaces, totalEdits, username) {
    $('#status').text(`Analysis complete. Total edits: ${totalEdits}`);
    
    // Sort by edit count
    const sorted = Object.entries(namespaces).sort((a, b) => b[1] - a[1]);
    
    let html = '<table style="border-collapse: collapse; width: 100%;">';
    html += '<tr style="background-color: #eaecf0;"><th style="border: 1px solid #a2a9b1; padding: 8px; text-align: left;">Namespace</th>';
    html += '<th style="border: 1px solid #a2a9b1; padding: 8px; text-align: right;">Edits</th>';
    html += '<th style="border: 1px solid #a2a9b1; padding: 8px; text-align: right;">Percentage</th></tr>';
    
    for (const [ns, count] of sorted) {
        const percentage = ((count / totalEdits) * 100).toFixed(2);
        
        // Get namespace ID
        let nsId = 0; // Main namespace
        if (ns !== 'Main') {
            const nsIdLookup = mw.config.get('wgNamespaceIds')[ns.toLowerCase().replace(/ /g, '_')];
            if (nsIdLookup !== undefined) {
                nsId = nsIdLookup;
            }
        }
        
        const contribUrl = mw.util.getUrl('Special:Contributions', {
            target: username,
            namespace: nsId
        });
        
        html += `<tr><td style="border: 1px solid #a2a9b1; padding: 8px;"><a href="${contribUrl}">${ns}</a></td>`;
        html += `<td style="border: 1px solid #a2a9b1; padding: 8px; text-align: right;">${count}</td>`;
        html += `<td style="border: 1px solid #a2a9b1; padding: 8px; text-align: right;">${percentage}%</td></tr>`;
    }
    
    html += '</table>';
    $('#results').html(html);
    
    drawPieChart(sorted, totalEdits);
}

function drawPieChart(data, total) {
    const canvas = document.getElementById('chart');
    const ctx = canvas.getContext('2d');
    
    canvas.style.display = 'block';
    
    const centerX = 200;
    const centerY = 200;
    const radius = 150;
    
    const colors = [
        '#3366CC', '#DC3912', '#FF9900', '#109618', '#990099',
        '#3B3EAC', '#0099C6', '#DD4477', '#66AA00', '#B82E2E',
        '#316395', '#994499', '#22AA99', '#AAAA11', '#6633CC'
    ];
    
    let currentAngle = -Math.PI / 2;
    
    for (let i = 0; i < data.length; i++) {
        const [ns, count] = data[i];
        const sliceAngle = (count / total) * 2 * Math.PI;
        
        ctx.beginPath();
        ctx.moveTo(centerX, centerY);
        ctx.arc(centerX, centerY, radius, currentAngle, currentAngle + sliceAngle);
        ctx.closePath();
        ctx.fillStyle = colors[i % colors.length];
        ctx.fill();
        
        // Add label
        const labelAngle = currentAngle + sliceAngle / 2;
        const labelX = centerX + Math.cos(labelAngle) * (radius * 0.7);
        const labelY = centerY + Math.sin(labelAngle) * (radius * 0.7);
        
        ctx.fillStyle = '#fff';
        ctx.font = 'bold 12px sans-serif';
        ctx.textAlign = 'center';
        ctx.textBaseline = 'middle';
        
        const percentage = ((count / total) * 100).toFixed(1);
        if (parseFloat(percentage) > 3) { // Only show label if slice is large enough
            ctx.fillText(`${percentage}%`, labelX, labelY);
        }
        
        currentAngle += sliceAngle;
    }
    
    // Add legend
    let legendY = 420;
    for (let i = 0; i < data.length; i++) {
        const [ns, count] = data[i];
        
        ctx.fillStyle = colors[i % colors.length];
        ctx.fillRect(20, legendY, 15, 15);
        
        ctx.fillStyle = '#000';
        ctx.font = '12px sans-serif';
        ctx.textAlign = 'left';
        ctx.fillText(`${ns} (${count})`, 40, legendY + 12);
        
        legendY += 20;
        
        if (legendY > canvas.height - 20) break;
    }
}
// </nowiki>