User:Admin

From AlphaX Wiki
Revision as of 05:09, 12 May 2026 by Admin (talk | contribs) (Enhanced Users by Country dashboard with day/week/month breakdown, activity chart, world map, and country table)
Jump to navigation Jump to search

Admin Management Tools

This page serves as a quick-reference hub for all administrative tools on AlphaX Wiki.

Users & Rights

Content Management

Recent Changes & Logs

Maintenance Reports

Media & Files

Data & Tools

Page Tools


Users by Country

Total Users
Countries / Regions
Active Today
Active This Week
Active This Month
 <button id="geo-btn-day" onclick="geoShowPeriod('day')" style="background:#ff6600; color:#fff; border:none; padding:8px 18px; border-radius:4px; cursor:pointer; font-weight:bold;">Today</button>
 <button id="geo-btn-week" onclick="geoShowPeriod('week')" style="background:#333; color:#eee; border:1px solid #555; padding:8px 18px; border-radius:4px; cursor:pointer;">This Week</button>
 <button id="geo-btn-month" onclick="geoShowPeriod('month')" style="background:#333; color:#eee; border:1px solid #555; padding:8px 18px; border-radius:4px; cursor:pointer;">This Month</button>
 <button id="geo-btn-all" onclick="geoShowPeriod('all')" style="background:#333; color:#eee; border:1px solid #555; padding:8px 18px; border-radius:4px; cursor:pointer;">All Time</button>
 
 <svg id="geo-world-map" viewBox="0 0 900 440" style="width:100%; height:auto; display:block;"></svg>
Users by Country
<thead> </thead> <tbody id="geo-table-body">
   </tbody>
# Country Users Share Edits
Loading...
Daily Activity (Last 30 Days)
 📍 Activity data is derived from wiki edit logs. Country detection uses Babel language tags on user pages. Users without Babel tags are shown as "Unknown".

<script> (function() {

 var GEO = window.AlphaXGeo = window.AlphaXGeo || {};
 var API = '/api.php';
 // ── helpers ──────────────────────────────────────────────────
 function isoDate(d) {
   return d.toISOString().slice(0,10);
 }
 function startOf(period) {
   var now = new Date();
   if (period === 'day') {
     return new Date(now.getFullYear(), now.getMonth(), now.getDate());
   } else if (period === 'week') {
     var d = new Date(now); d.setDate(d.getDate() - d.getDay());
     d.setHours(0,0,0,0); return d;
   } else if (period === 'month') {
     return new Date(now.getFullYear(), now.getMonth(), 1);
   }
   return new Date(0);
 }
 // ── country flags & names ─────────────────────────────────────
 var COUNTRY_MAP = {
   'en':'🇺🇸 English / US','de':'🇩🇪 German','fr':'🇫🇷 French','es':'🇪🇸 Spanish',
   'it':'🇮🇹 Italian','pt':'🇧🇷 Portuguese','ru':'🇷🇺 Russian','ja':'🇯🇵 Japanese',
   'zh':'🇨🇳 Chinese','ko':'🇰🇷 Korean','ar':'🇸🇦 Arabic','nl':'🇳🇱 Dutch',
   'pl':'🇵🇱 Polish','sv':'🇸🇪 Swedish','no':'🇳🇴 Norwegian','da':'🇩🇰 Danish',
   'fi':'🇫🇮 Finnish','tr':'🇹🇷 Turkish','cs':'🇨🇿 Czech','hu':'🇭🇺 Hungarian',
   'ro':'🇷🇴 Romanian','uk':'🇺🇦 Ukrainian','vi':'🇻🇳 Vietnamese','th':'🇹🇭 Thai',
   'id':'🇮🇩 Indonesian','ms':'🇲🇾 Malay','he':'🇮🇱 Hebrew','fa':'🇮🇷 Persian',
   'hi':'🇮🇳 Hindi','bn':'🇧🇩 Bengali'
 };
 // ── fetch all users ───────────────────────────────────────────
 function fetchUsers() {
   return fetch(API + '?action=query&list=allusers&aulimit=500&format=json&origin=*')
     .then(function(r){return r.json();})
     .then(function(d){ return d.query.allusers || []; });
 }
 // ── fetch recent changes for activity ────────────────────────
 function fetchRecentChanges(rcstart, rclimit) {
   rclimit = rclimit || 500;
   var url = API + '?action=query&list=recentchanges&rclimit=' + rclimit +
     '&rcprop=user|timestamp&format=json&origin=*';
   if (rcstart) url += '&rcstart=' + rcstart;
   return fetch(url).then(function(r){return r.json();})
     .then(function(d){ return d.query.recentchanges || []; });
 }
 // ── fetch babel info for a user ───────────────────────────────
 function fetchUserBabel(username) {
   var url = API + '?action=query&titles=User:' + encodeURIComponent(username) +
     '&prop=categories&format=json&origin=*';
   return fetch(url).then(function(r){return r.json();})
     .then(function(d){
       var pages = d.query && d.query.pages ? d.query.pages : {};
       var cats = [];
       Object.values(pages).forEach(function(p){
         (p.categories || []).forEach(function(c){ cats.push(c.title); });
       });
       // Look for Category:User XX patterns
       var langs = [];
       cats.forEach(function(cat){
         var m = cat.match(/Category:User ([a-z]{2,3})/i);
         if (m) langs.push(m[1].toLowerCase());
       });
       return langs;
     });
 }
 // ── world map SVG (simplified) ────────────────────────────────
 var COUNTRY_SHAPES = {
   'de': 'M440,145 L455,140 L465,148 L462,165 L448,170 L438,162 Z',
   'fr': 'M415,152 L430,148 L438,162 L430,175 L415,172 L408,162 Z',
   'es': 'M390,170 L415,168 L415,185 L395,190 L385,182 Z',
   'it': 'M450,168 L462,165 L468,180 L460,195 L450,188 L445,178 Z',
   'gb': 'M418,135 L428,132 L430,145 L420,148 L414,142 Z',
   'ru': 'M460,120 L560,110 L580,135 L540,145 L465,148 Z',
   'us': 'M120,150 L220,148 L225,190 L200,200 L115,195 Z',
   'cn': 'M620,150 L680,145 L685,175 L650,185 L615,178 Z',
   'jp': 'M695,155 L708,152 L710,165 L700,168 L693,163 Z',
   'br': 'M195,230 L240,225 L245,275 L215,282 L188,270 Z',
   'in': 'M580,175 L615,170 L618,210 L595,218 L575,205 Z',
   'au': 'M660,280 L720,275 L725,320 L690,328 L655,315 Z',
 };
 function drawWorldMap(highlightLangs) {
   var svg = document.getElementById('geo-world-map');
   if (!svg) return;
   // Simple world outline
   svg.innerHTML = '<rect width="900" height="440" fill="#1a1a1a"/>' +
     '<text x="450" y="30" fill="#444" text-anchor="middle" font-size="12" font-family="sans-serif">World Activity Map (language-based approximation)</text>';
   // Draw ocean
   var ocean = document.createElementNS('http://www.w3.org/2000/svg','rect');
   ocean.setAttribute('width','900'); ocean.setAttribute('height','440');
   ocean.setAttribute('fill','#0d1f33'); svg.appendChild(ocean);
   // Draw continent blobs
   var continents = [
     // North America
     'M80,100 L240,95 L255,200 L220,230 L170,235 L110,220 L75,170 Z',
     // South America
     'M155,240 L220,235 L235,360 L195,385 L155,370 L138,320 Z',
     // Europe
     'M385,90 L480,85 L490,160 L450,175 L400,170 L378,140 Z',
     // Africa
     'M390,175 L460,170 L470,320 L430,345 L388,335 L375,280 Z',
     // Asia
     'M490,80 L750,75 L760,230 L700,250 L520,240 L485,180 Z',
     // Australia
     'M640,280 L740,275 L748,355 L700,368 L638,355 Z',
     // Greenland
     'M230,50 L290,45 L295,90 L255,95 L228,82 Z',
   ];
   continents.forEach(function(d){
     var path = document.createElementNS('http://www.w3.org/2000/svg','path');
     path.setAttribute('d', d);
     path.setAttribute('fill', '#2a3a2a');
     path.setAttribute('stroke', '#1a2a1a');
     path.setAttribute('stroke-width', '1');
     svg.appendChild(path);
   });
   // Highlight active countries
   if (highlightLangs && highlightLangs.length > 0) {
     Object.keys(COUNTRY_SHAPES).forEach(function(cc){
       var path = document.createElementNS('http://www.w3.org/2000/svg','path');
       path.setAttribute('d', COUNTRY_SHAPES[cc]);
       var isActive = highlightLangs.some(function(l){ return l.startsWith(cc); });
       path.setAttribute('fill', isActive ? '#ff6600' : '#3a4a3a');
       path.setAttribute('stroke', '#555');
       path.setAttribute('stroke-width', '1');
       path.setAttribute('opacity', isActive ? '0.9' : '0.5');
       svg.appendChild(path);
     });
   }
   // Dots for known locations
   var locations = [
     {x:160, y:165, label:'North America'},
     {x:450, y:130, label:'Europe'},
     {x:620, y:160, label:'Asia'},
     {x:430, y:260, label:'Africa'},
     {x:190, y:300, label:'South America'},
     {x:690, y:320, label:'Oceania'},
   ];
   locations.forEach(function(loc){
     var text = document.createElementNS('http://www.w3.org/2000/svg','text');
     text.setAttribute('x', loc.x); text.setAttribute('y', loc.y);
     text.setAttribute('fill', '#444'); text.setAttribute('font-size', '10');
     text.setAttribute('font-family', 'sans-serif');
     text.setAttribute('text-anchor', 'middle');
     text.textContent = loc.label;
     svg.appendChild(text);
   });
 }
 // ── render table ──────────────────────────────────────────────
 function renderTable(rows, total, period) {
   var tbody = document.getElementById('geo-table-body');
   var titleEl = document.getElementById('geo-table-title');
   if (!tbody) return;
   var labels = {day:'Today', week:'This Week', month:'This Month', all:'All Time'};
   if (titleEl) titleEl.textContent = 'Users by Country – ' + (labels[period] || 'All Time');
   if (rows.length === 0) {

tbody.innerHTML = 'No activity data for this period.';

     return;
   }
   var html = ;
   rows.forEach(function(row, i) {

var bar = '

' +

       '' + row.pct.toFixed(1) + '%';
     var rowBg = i % 2 === 0 ? '#1a1a1a' : '#1e1e1e';

html += '' + '' + (i+1) + '' + '' + (COUNTRY_MAP[row.lang] || ('🌐 ' + row.lang)) + '' + '' + row.users + '' + '' + bar + '' + '' + row.edits + '' + ''; }); tbody.innerHTML = html; } // ── render activity bars ─────────────────────────────────────── function renderActivityBars(dailyData) { var wrap = document.getElementById('geo-activity-bars'); var labelWrap = document.getElementById('geo-activity-labels'); if (!wrap) return; var max = Math.max.apply(null, Object.values(dailyData).concat([1])); var days = []; for (var i = 29; i >= 0; i--) { var d = new Date(); d.setDate(d.getDate() - i); days.push(isoDate(d)); } var barsHtml = , labelsHtml = ; days.forEach(function(day, idx) { var val = dailyData[day] || 0; var h = Math.max(3, Math.round((val / max) * 70)); var isToday = idx === 29; var color = isToday ? '#ff6600' : (val > 0 ? '#cc5500' : '#222'); barsHtml += '

';

     if (idx % 5 === 0 || isToday) {

labelsHtml += '

' + day.slice(5) + '

';

     } else {

labelsHtml += '

';

     }
   });
   wrap.innerHTML = barsHtml;
   labelWrap.innerHTML = labelsHtml;
 }
 // ── main data load ────────────────────────────────────────────
 var cachedUsers = null;
 var cachedChanges = null;
 var cachedBabel = {};
 var currentPeriod = 'day';
 function loadDashboard() {
   var now = new Date();
   Promise.all([
     fetchUsers(),
     fetchRecentChanges(null, 500)
   ]).then(function(results) {
     var users = results[0];
     var changes = results[1];
     cachedUsers = users;
     cachedChanges = changes;
     document.getElementById('geo-total').textContent = users.length;
     document.getElementById('geo-updated').textContent = 'Updated: ' + now.toLocaleTimeString();
     // Count activity periods
     var dayStart = startOf('day').toISOString();
     var weekStart = startOf('week').toISOString();
     var monthStart = startOf('month').toISOString();
     var activeDay = new Set(), activeWeek = new Set(), activeMonth = new Set();
     var daily = {};
     changes.forEach(function(c) {
       if (c.timestamp >= dayStart) activeDay.add(c.user);
       if (c.timestamp >= weekStart) activeWeek.add(c.user);
       if (c.timestamp >= monthStart) activeMonth.add(c.user);
       var day = c.timestamp.slice(0,10);
       daily[day] = (daily[day] || 0) + 1;
     });
     document.getElementById('geo-active-day').textContent = activeDay.size;
     document.getElementById('geo-active-week').textContent = activeWeek.size;
     document.getElementById('geo-active-month').textContent = activeMonth.size;
     renderActivityBars(daily);
     // Fetch babel for all real users (skip bots)
     var humanUsers = users.filter(function(u){
       return !['Lucy','Babel AutoCreate','Delete page script','FuzzyBot','Maintenance script','MediaWiki default'].includes(u.name) || u.name === 'Lucy' || u.name === 'Admin';
     });
     var babelPromises = humanUsers.map(function(u) {
       return fetchUserBabel(u.name).then(function(langs) {
         cachedBabel[u.name] = langs;
       });
     });
     Promise.all(babelPromises).then(function() {
       geoShowPeriod(currentPeriod);
     });
   }).catch(function(e) {
     console.error('GEO dashboard error:', e);
   });
 }
 window.geoShowPeriod = function(period) {
   currentPeriod = period;
   var btns = ['day','week','month','all'];
   btns.forEach(function(p) {
     var btn = document.getElementById('geo-btn-' + p);
     if (!btn) return;
     if (p === period) {
       btn.style.background = '#ff6600'; btn.style.color = '#fff';
       btn.style.border = 'none'; btn.style.fontWeight = 'bold';
     } else {
       btn.style.background = '#333'; btn.style.color = '#eee';
       btn.style.border = '1px solid #555'; btn.style.fontWeight = 'normal';
     }
   });
   if (!cachedChanges || !cachedUsers) return;
   var cutoff = period === 'all' ? '1970-01-01' : startOf(period).toISOString();
   // Build user -> edit count map for period
   var userEdits = {};
   cachedChanges.forEach(function(c) {
     if (c.timestamp >= cutoff) {
       userEdits[c.user] = (userEdits[c.user] || 0) + 1;
     }
   });
   // Map languages to user counts
   var langUsers = {}, langEdits = {};
   var withBabel = 0;
   cachedUsers.forEach(function(u) {
     var langs = cachedBabel[u.name] || [];
     if (langs.length === 0) langs = ['unknown'];
     else withBabel++;
     var edits = userEdits[u.name] || 0;
     langs.slice(0,1).forEach(function(lang) {
       langUsers[lang] = (langUsers[lang] || 0) + 1;
       langEdits[lang] = (langEdits[lang] || 0) + edits;
     });
   });
   var total = cachedUsers.length;
   document.getElementById('geo-countries').textContent = Object.keys(langUsers).filter(function(l){return l!=='unknown';}).length;
   var rows = Object.keys(langUsers).map(function(lang) {
     return { lang: lang, users: langUsers[lang], edits: langEdits[lang] || 0,
              pct: total > 0 ? (langUsers[lang] / total * 100) : 0 };
   }).sort(function(a,b){ return b.users - a.users; });
   // Move unknown to bottom
   rows.sort(function(a,b) {
     if (a.lang === 'unknown') return 1;
     if (b.lang === 'unknown') return -1;
     return b.users - a.users;
   });
   renderTable(rows, total, period);
   drawWorldMap(rows.filter(function(r){return r.lang!=='unknown';}).map(function(r){return r.lang;}));
 };
 // ── boot ─────────────────────────────────────────────────────
 if (document.readyState === 'loading') {
   document.addEventListener('DOMContentLoaded', loadDashboard);
 } else {
   loadDashboard();
 }

})(); </script>