اینجا نسخه‌ای پشتیبان از الگوریتمی که برای فهرست کردن کاربران واجد شرایط رأی‌دادن به کار گرفته شده نگهداری می‌شود تا در سال‌های بعد قابل دسترسی باشد.

پیشینه

ویرایش

تا پیش از دورهٔ یازدهم، فهرست رأی‌مندان با کمک یک پرسمان از پایگاه داده قابل تهیه بود. برای دیدن پرسمان می‌توانید به تاریخچهٔ همین صفحه مراجعه کنید.

از تاریخ ۳ ژوئن ۲۰۱۹ به این سو، به دلیل تغییراتی که در ساختار پایگاه دادهٔ ویکی در جریان است انجام پرسمان‌های پیچیده زمان زیادی می‌برد؛ از جمله، پرسمان مربوط به فهرست رأی‌مندان عملاً غیرقابل اجرا شد. لذا از این سال روال استخراج رأی‌مندان به دو مرحله شکسته شد. مرحلهٔ اول یک پرسمان است که فهرست کاربرانی که برخی از شرایط را دارا هستند استخراج می‌کند. مرحلهٔ دوم یک کد جاوااسکریپت است که با کمک خروجی مرحلهٔ اول، به بررسی باقی شرایط می‌پردازد. این کد را باید از طریق جاوااسکریپت کاربری به کار بست و خروجی‌اش در ویژه:صفحهٔ خالی قابل دیدن است.

پرسمان

ویرایش

مرحلهٔ اول

ویرایش

پرسمان زیر فهرستی از کاربرانی را تهیه می‌کند که شرط‌های ۱ و ۳ از وپ:رگه را دارا هستند. شرط ۲ بعداً در مرحلهٔ دوم بررسی می‌شود.

در پرسمان زیر، تاریخ‌ها (که به صورت برچسب زمان آمده‌اند) باید هر سال اصلاح شوند تا بازهٔ زمانی صحیح را منعکس کنند.

use fawiki_p;
select
  user_id,
  user_name
from user
join actor
  on actor_user = user_id
join 
(
  select rev_actor
  from revision_userindex
  join page
    on page_id = rev_page
  where
    page_namespace = 0
    and rev_timestamp > '20221009000000' -- last 12 months leading to nomination start date
    and rev_timestamp < '20231009000000'
  group by rev_actor
  having count(*) >= 100
) onehundred -- at least 100 edits in main namespace
  on actor_id = onehundred.rev_actor
where
  (
    exists (
      select 1
      from logging_userindex
      where log_actor = actor_id
      and log_type = 'newusers'
      and log_action in ('create', 'autocreate')
      and log_timestamp < '20230709000000' -- occurred 3 or more months prior to nomination start date
    )
    or exists (
      select 1
      from revision_userindex
      where rev_actor = actor_id
      and rev_timestamp < '20230709000000' -- occurred 3 or more months prior to nomination start date
    )
  ) and
  not exists (
    select 1
    from user_groups
    where ug_user = user_id
    and ug_group = 'bot'
  )
order by
  user_id,
  user_name

مرحلهٔ دوم

ویرایش

کد جاوااسکریپت زیر باید اصلاح شود و مقدار متغیر users به آرایه‌ای تغییر یابد که عناصرش نام‌های کاربری‌ای هستند که از مرحلهٔ اول به دست آمدند. کد را می‌توانید در ویژه:صفحه من/common.js قرار بدهید تا برای حساب شما اجرا شود. برای دیدن نتیجه باید به ویژه:صفحهٔ خالی بروید. توجه کنید که این کد صدها درخواست به سرور می‌فرستد در نتیجه بهتر است فقط وقتی که کاملاً ضرورت دارد از آن استفاده کنید (مثلاً اگر مجری انتخابات هستید، یا قرار است کار مجری را ارزیابی کنید).

(function($, mw) {
	if (mw.config.get('wgCanonicalSpecialPageName') !== 'Blankpage') return;
	mw.loader.using('mediawiki.api').then(function(){
		var ucstart = '2020-10-12T00:00:00.000Z';
		var ucend = '2019-10-12T00:00:00.000Z';
		var threshold = 500;
		var users = [
			'Huji',
			'ThisThat'
		];
		
		$('#mw-content-text').empty();
		$('#mw-content-text').append($('<table>').addClass('wikitable'));
		$('#mw-content-text table').append($('<tr>').append($('<th>').text('User')));

		users.forEach(function(user){
			var api = new mw.Api();
			api.get({
				action: 'query',
				list: 'usercontribs',
				ucuser: user,
				ucnamespace: 0,
				uclimit: threshold
			}).done(function(data) {
				if(data.query.usercontribs.length == threshold){
					$('#mw-content-text table').append($('<tr>').append($('<td>').text(user)));
				}
			});
		});
	});
})(jQuery, mediaWiki);

کنترل نتایج

ویرایش

با کمک ابزاری که در https://huji.toolforge.org قرار داده شده، می‌توان یک نام کاربری وارد کرد و واجد شرایط بودن آن کاربر برای رأی دادن را ارزیابی کرد. کد این ابزار در پایین آمده‌است و هر سال باید از نظر پرسمان‌هایی که به کار می‌برد روزآمدسازی شود.

<!DOCTYPE html>
<?php
$username = '';
$error = '';

$ts_pw = posix_getpwuid(posix_getuid());
$ts_mycnf = parse_ini_file($ts_pw['dir'] . "/replica.my.cnf");
$nfi = new \NumberFormatter('fa-IR', \NumberFormatter::IGNORE);
$nfd = new \NumberFormatter('fa-IR', \NumberFormatter::DECIMAL);

if ( isset( $_POST['username'] ) ) {
  $username = str_replace('"', '', $_POST['username']);
  $prefix = '/^(User:|کاربر:)/';
  $username = preg_replace($prefix, '', $username);
  $username = ucfirst(trim($username));
  
  $mysqli = new mysqli('fawiki.analytics.db.svc.eqiad.wmflabs', $ts_mycnf['user'], $ts_mycnf['password'], 'fawiki_p');
  $q = $mysqli->prepare('SELECT actor_id, actor_name FROM actor WHERE actor_name=?');
  $q->bind_param("s", $username);
  $q->execute();
  $q->bind_result($actor_id, $actor_name);
  $q->fetch();
  if($username === '') {
    $error = 'No username provided';
  }
  elseif(!isset($actor_id)) {
    $error = 'User does not exist!';
  } else {
    // Criterion 1
    unset($q);
    $q = $mysqli->prepare("SELECT MIN(log_timestamp) FROM logging_userindex WHERE log_type = 'newusers' AND log_action IN ('create', 'autocreate') AND log_actor=?");
    $q->bind_param("i", $actor_id);
    $q->execute();
    $q->bind_result($mints);
    $q->fetch();
    if(!isset($mints)) {
      // Try estimating account creation time using edits instead
      unset($q);
      $q = $mysqli->prepare('SELECT MIN(rev_timestamp) FROM revision_userindex WHERE rev_actor=?');
      $q->bind_param("i", $actor_id);
      $q->execute();
      $q->bind_result($mints);
      $q->fetch();
    }
    if(isset($mints) && strlen($mints) == 14){
      $year = $nfi->format(substr($mints, 0, 4));
      $month = $nfi->format(intval(substr($mints, 4, 2)));
      $day = $nfi->format(intval(substr($mints, 6, 2)));

      $mints =  $year . '٫' . $month . '٫' . $day;
    }
    // Criterion 2
    unset($q);
    $q = $mysqli->prepare("SELECT COUNT(*) FROM revision_userindex JOIN page ON page_id = rev_page AND page_namespace = 0 WHERE rev_timestamp < 20201012000000 AND rev_actor=?");
    $q->bind_param("i", $actor_id);
    $q->execute();
    $q->bind_result($edits);
    $q->fetch();
    $edits = $nfd->format($edits);
    // Criterion 3
    unset($q);
    $q = $mysqli->prepare("SELECT COUNT(*) FROM revision_userindex JOIN page ON page_id = rev_page AND page_namespace = 0 WHERE rev_timestamp < 20201012000000 AND rev_timestamp > 20191012000000 AND rev_actor=?");
    $q->bind_param("i", $actor_id);
    $q->execute();
    $q->bind_result($recentedits);
    $q->fetch();
    $recentedits = $nfd->format($recentedits);
  }
}
?>
<html lang="fa">
<head>
  <meta charset="utf-8">
  <meta content="width=device-width, initial-scale=1, shrink-to-fit=no" name="viewport">
  <meta content="" name="description">
  <meta content="" name="author">
  <link href="" rel="icon">
  <title>Voter Eligibility Check</title>
  <link href="https://cdn.rtlcss.com/bootstrap/v4.2.1/css/bootstrap.min.css" rel="stylesheet">
</head>
<body dir="rtl" style="direction:rtl">
  <div id="wrapper">
    <div id="page-content-wrapper">
      <nav class="navbar navbar-expand-lg navbar-dark bg-secondary border-bottom">
        <div class="container">
          <a class="navbar-brand" href="./">ابزارهای حجت</a>
        </div>
      </nav>
      <div class="container">
        <h1 class="mt-4">ارزیابی شرایط رأی‌مندی</h1>
        <p>این فرم به شما کمک می‌کند که وضعیت یک کاربر ویکی‌پدیای فارسی را از نظر برخورداری شرایط رأی‌مندی در <a href=
        "https://fa.wikipedia.org/wiki/%D9%88%D9%BE:%D9%86%D8%A7%D8%B8%D8%B1%DB%B1%DB%B1">انتخابات دور یازدهم هیئت
        نظارت</a> بررسی کنید.</p>
        <p>شرایط لازم برای برخورداری از حق رأی (رأی‌مندی) در <a href=
        "https://fa.wikipedia.org/wiki/%D9%88%D9%BE:%D8%B1%DA%AF%D9%87">اینجا</a> در دسترس هستند. این ابزار صرفاً برای
        کمک به ارزیابی آن شرایط ساخته شده‌است و نباید به عنوان منبع اصلی تعریف شرایط رأی‌مندی در نظر گرفته شود. این
        ابزار با راندن پرسمان‌هایی بر روی نسخهٔ کپی پایگاه دادهٔ ویکی‌مدیا (موسوم به Wikireplica Databases) اطلاعاتی را
        که برای ارزیابی شرایط رأی‌مندی می‌تواند مفید باشد استخراج می‌کند. توجه کنید که هر پرسمان بین چند ثانیه تا چند
        دقیقه طول می‌کشد و مدت آن برای کاربران قدیمی‌تر طولانی‌تر است.</p>
        <form action="./" method="post">
          <div class="form-group">
            <label for="username">نام کاربری</label> <input aria-describedby="usernameHelp" class="form-control" id=
            "username" name="username" placeholder="نام کاربری را وارد کنید" type="text" value=
            "<?php echo $username; ?>"> <small class="form-text text-muted" id="usernameHelp">نیازی به پیشوند «کاربر:»
            نیست.</small>
          </div><button class="btn btn-primary" type="submit">ارسال</button>
        </form>
        <hr>
        <?php if($error !== ''): ?>
        <div class="card mb-12 bg-danger">
          <div class="card-header text-white">
            خطا
          </div>
          <div class="card-body bg-white">
            <h5 class="card-title">درخواست شما شکست خورد</h5>
            <div class="card-text">
              <?php echo $error; ?>
            </div>
          </div>
        </div><?php elseif(isset($mints)): ?>
        <div class="card mb-12 bg-success">
          <div class="card-header text-white">
            نتایج
          </div>
          <div class="card-body bg-white">
            <h5 class="card-title">ارزیابی شرایط رأی‌مندی</h5>
            <div class="card-text">
              <p>این اطلاعات برای حساب <bdi><?php echo $username; ?></bdi> به دست آمد:</p>
              <table class="table table-bordered table-striped">
                <thead>
                  <tr>
                    <th>پرسمان</th>
                    <th>نتیجه</th>
                    <th>شرط لازم برای رأی‌مندان</th>
                  </tr>
                </thead>
                <tbody>
                  <tr>
                    <td>تاریخ ایجاد حساب</td>
                    <td><?php echo $mints; ?></td>
                    <td>پیش از ۲۰۲۰٫۷٫۱۲</td>
                  </tr>
                  <tr>
                    <td>ویرایش در مقاله‌ها پیش از شروع انتخابات</td>
                    <td><?php echo $edits; ?></td>
                    <td>دست کم ۵۰۰</td>
                  </tr>
                  <tr>
                    <td>ویرایش در مقاله‌ها در یک سال منتهی به انتخابات</td>
                    <td><?php echo $recentedits; ?></td>
                    <td>دست کم ۱۰۰</td>
                  </tr>
                </tbody>
              </table>
              <p>برای دیدن سیاههٔ قطع دسترسی این کاربر <a href=
              "https://fa.wikipedia.org/w/index.php?title=Special:Logs&page=User:%3C?php%20echo%20urlencode($username);%20?%3E&type=block">
              اینجا</a> کلیک کنید.</p>
            </div>
          </div>
        </div><?php endif; ?>
      </div>
    </div>
  </div>
</body>
</html>

همچنین با کمک اسکریپتی مثل زیری می‌شود اطلاعات مربوط به رأی‌مندان را استخراج و در ویکی منتشر کرد:

#!/usr/bin/python
# -*- coding: utf-8  -*-
"""
voters.py - a script to fetch detailed data on fawiki election voters
"""
#
# (C) w:fa:User:Huji, 2020
#
# Distributed under the terms of the MIT license.
#

import MySQLdb

users = [
    'User One',
    'User Two'
]

getactor = "SELECT actor_id FROM actor WHERE actor_name = '%s'"
getcreation = """
SELECT MIN(log_timestamp)
FROM logging_userindex
WHERE
    log_type = 'newusers'
    AND log_action
    IN (
        'create',
        'autocreate'
    )
    AND log_actor=%s
"""
getfirstedit = """
SELECT MIN(rev_timestamp)
FROM revision_userindex
WHERE rev_actor=%s
"""
getedits = """
SELECT COUNT(*)
FROM revision_userindex
JOIN page
    ON page_id = rev_page
    AND page_namespace = 0
WHERE
    rev_timestamp < 20201012000000
    AND rev_actor=%s
"""
getrecentedits = """
SELECT COUNT(*)
FROM revision_userindex
JOIN page
    ON page_id = rev_page
    AND page_namespace = 0
WHERE
    rev_timestamp < 20201012000000
    AND rev_timestamp > 20191012000000
    AND rev_actor=%s
"""

conn = MySQLdb.connect(
    host='fawiki.labsdb',
    db='fawiki_p',
    read_default_file='~/replica.my.cnf'
)
cursor = conn.cursor()
out = ''

for user in users:
    print('                                        ', end='\r')
    print('Processing %s' % user, end='\r')
    sql = getactor % user
    cursor.execute(sql.encode('utf-8'))
    results = cursor.fetchall()
    actor_id = results[0]

    sql = getcreation % actor_id
    cursor.execute(sql)
    results = cursor.fetchall()

    if results[0][0] is None:
        sql = getfirstedit % actor_id
        cursor.execute(sql)
        results = cursor.fetchall()

    mints = results[0][0].decode('utf-8')
    mints = "%s-%s-%s" % (mints[0:4], mints[4:6], mints[6:8])

    sql = getedits % actor_id
    cursor.execute(sql)
    results = cursor.fetchall()
    edits = results[0][0]

    sql = getrecentedits % actor_id
    cursor.execute(sql)
    results = cursor.fetchall()
    recentedits = results[0][0]

    out += '| [[User:%s|%s]] || %s || %s || %s\n|-\n' % (
        user,
        user,
        mints,
        edits,
        recentedits
    )

print('                                        ')
print(out)

سایر پرسمان‌ها

ویرایش

مطابق وپ:شعه، نامزدها باید دست کم ۱۰۰۰ ویرایش غیرجزئی در فضای نام اصلی داشته باشند. پرسمانی شبیه زیر را می‌توان برای تأیید این مسئله استفاده کرد.

USE fawiki_p;

SELECT
  actor_name,
  COUNT(*)
FROM revision_userindex
JOIN page
  ON rev_page = page_id
JOIN actor_revision
  ON rev_actor = actor_id
WHERE
  page_namespace = 0
  AND rev_minor_edit = 0
  AND rev_timestamp < '20201012000000'
  AND actor_name IN (
    'This User',
    'That User'  
  )
GROUP BY rev_actor
ORDER BY COUNT(*)