#!/usr/bin/python
# -*- coding: utf-8 -*-
"""
dar.py - a script to move categories by request
useage:
python pwb.py dar [OPTIONS]
"""
# (C) Reza (w:fa:User:Reza1615), 2015
# (C) Amir (w:fa:User:Ladsgroup), 2015
# (C) Huji (w:fa:User:Huji), 2016
# (C) Mohib (w:ur:User:mohibalvi), 2024
# Distributed under the terms of MIT License (MIT)
from __future__ import absolute_import, unicode_literals
import pywikibot
from pywikibot import pagegenerators
from pywikibot import config
import sys
import json
import re
from scripts import category
import pathlib
class CatMoveBot:
def __init__(self, logPage=None, project='wikipedia'):
if logPage is None:
raise ValueError('Log page must be specified')
self.logPage = logPage
self.site = pywikibot.Site('ur', project)
self.redirTemplate = 'زمرہ رجوع مکرر'
# Initialize summary template without .format()
self.summary = '[[وپ:دمز|خودکار: منتقلی زمرہ]] بدرخواست [[User:%s|%s]]'
def move(self, origin, destination, user):
"""
Plans moving pages from one category to the other, and updates the category
pages to reflect this move.
@param task: A list with two elements; the first element is the name of the
origin category, and the second is the name of, family=project the destination category
@param user: Name of the user on whose behalf the category move is done
"""
# Adjust summary based on whether origin and destination start with "زمرہ:"
summary = f'[[وپ:دمز|خودکار: منتقلی زمرہ]] بدرخواست [[User:{user}|{user}]]'
if origin.startswith("زمرہ:") and destination.startswith("زمرہ:"):
summary += f' از [[{origin}]] > [[{destination}]]'
elif origin.startswith("زمرہ:"):
summary += f' از [[{origin}]] > [[زمرہ:{destination}]]'
elif destination.startswith("زمرہ:"):
summary += f' از [[زمرہ:{origin}]] > [[{destination}]]'
else:
summary += f' از [[زمرہ{origin}]] > [[زمرہ{destination}]]'
comment = summary
cat = category.CategoryMoveRobot(
origin, destination, batch=True,
comment=comment, inplace=False, move_oldcat=True,
delete_oldcat=True, title_regex=None, history=False)
cat.run()
def run(self, task, user):
origin = task[0]
destination = task[1]
# Title of the destination page, without prefix
destTitle = re.sub('^(زمرہ|[Cc]ategory)\:', '', destination)
originPage = pywikibot.Page(self.site, origin)
destinationPage = pywikibot.Page(self.site, destination)
#originPageText = ''
#destinationPageText = ''
if originPage:
try:
originPageText = originPage.get()
# Replace contents with the {{Category redirect}} template
originPage.put(
'{{' + self.redirTemplate + '|' + destTitle + '}}',
self.summary % (user, user, origin, destination)) # corrected and replaced "destinatino" with destination
# Added Exception as err code for modern error handling
except Exception as err:
# Failed to fetch page contents, changed pass to print(err) to print the error
print(err)
if destinationPage:
try:
originPageText = originPage.get()
# TODO: Remove old {{Category redirect}}
except Exception as err:
print(err) # changed pass to print(err) to print the error
self.move(origin, destination, user)
originPage = pywikibot.Page(self.site, origin)
class CatMoveInput:
def __init__(self, cacheFile=None, project='wikipedia', threshold=3000):
"""
@param cacheFile: path to the local cache of previously validated users
"""
if cacheFile is None:
raise ValueError('Cache file location must be specified')
else:
self.cacheFile = cacheFile
self.cache = self.loadCache()
self.site = pywikibot.Site('ur', project)
self.tasksPageDefault = '{{/بالا}}'
self.moverBots = ['ZumraBot']
self.threshold = threshold
self.successSummary = 'خودکار : منتقلی زمرہ مکمل!'
def loadCache(self):
f = open(self.cacheFile, 'r', encoding='utf-8') # removed codecs as open now supports utf-8 encoding
txt = f.read().strip()
f.close()
if txt == '':
# Brand new cache file will fail json.loads(), return an empty dictionary instead
cache = {}
else:
cache = json.loads(txt)
return cache
def updateCache(self, cache):
fh = open(self.cacheFile, 'w', encoding='utf-8')
fh.write(json.dumps(cache))
fh.close()
def verifyUser(self, username):
username = username.replace(' ', '_')
# If we have already established that this user qualifies then don't verify the user again
if self.cache.get(username):
return True
# Only users whose edit count is larger than self.threshold can request category moves
params = {
'action': 'query',
'list': 'users',
'ususers': username,
'usprop': 'editcount'
}
try:
req = pywikibot.data.api.Request(site=self.site, **params)
query = req.submit()
if query['query']['users'][0]['editcount'] > self.threshold:
self.cache[username] = True
self.updateCache(self.cache)
return True
else:
return False
except:
return False
def processInput(self, tasksPageName):
tasksPage = pywikibot.Page(self.site, tasksPageName)
try:
pageText = tasksPage.get()
# replaced getVersionHistory() with revisions()
#revisions = tasksPage.revisions()
#pageHistory = list(revisions)
#lastUser = pageHistory[-1]['user']
# Fetch only the latest revision
ver_history = tasksPage.revisions(reverse=True,total=1)
# ver_history is a generator, use next method to get iterator
last_revision = next(ver_history)
lastUser = last_revision['user']
# replaced IsRedirectPage() to isRedirectPage()
except pywikibot.isRedirectPage:
tasksPage = tasksPage.getRedirectTarget()
try:
pageText = tasksPage.get()
# Fetch only the latest revision
ver_history = tasksPage.revisions(reverse=True,total=1)
last_revision = next(ver_history)
lastUser = last_revision['user']
except:
raise ValueError('Task list page not found!')
except:
raise ValueError('Task list page not found!')
if lastUser in self.moverBots:
print(json.dumps({
'result': 'Last edit was by a mover bot. Request ignored.'
}))
return False
elif self.verifyUser(lastUser):
print(json.dumps({ 'result': 'User verified. Processing task list.' }))
tasks = self.getTaskList(pageText)
tasksPage.put(self.tasksPageDefault, self.successSummary)
return {'tasks': tasks, 'user': lastUser}
else:
print(json.dumps({
'result': 'Last editor was not qualified. Request ignored.' }))
return False
def getTaskList(self, taskText):
"""
Returns a list of lists, where each inner lists describe one category move request (i.e. [origin, destination]).
@param taskText: wikicode of the page containing category move requests
"""
# Commenting out text replacement as bala template is required
lines = taskText.split('\n')
wikiText = [line for line in lines if line.strip()]
#taskText = taskText.replace('{{/بالا}}','').replace('\r','').replace('\n\n','\n').strip()
taskList = []
for line in wikiText:
# change from line == '*' to line.startswith('*)
if line.startswith('*'):
# Remove the * and any immediately following spaces
line = re.sub('^\* *', '', line)
# Unlink category links
if '[[' in line:
line = re.sub('\[\[\:(زمرہ|[Cc]ategory)\:([^\]]+)\]\]',
'\\1:\\2', line)
# Split by '>' or '@' (optionally surrounded by spaces)
if '>' in line or '@' in line:
pieces = re.split(' *[>@] *', line)
# Clean up category mentions
for i in range(0, len(pieces)):
# Make edit summaries more beautiful!
pieces[i] = pieces[i].replace('_', ' ')
# Add missing `Category` prefix
if (
re.search('^[Cc]ategory\:', pieces[i]) is None and
re.search('^زمرہ\:', pieces[i]) is None
):
pieces[i] = ':' + pieces[i]
# Add the pair to our task list
taskList.append(pieces)
else:
# Mention @ or > not found in the line
print('No ">" or "@" characters in the line')
return taskList
def main():
from pathlib import Path
BASE_DIR = Path(__file__).resolve().parent
cacheFile = BASE_DIR / 'cache_dar.txt' # changed it to run locally
#cacheFile = '/data/project/zumraband/cache_dar.txt'
# if 'wikiquote' in sys.argv:
# project = 'wikiquote'
# page_name = 'ویکی اقتباسات:درخواست منتقلی زمرہ جات'
# limit = 750
# config.family = project
# else:
project = 'wikipedia'
page_name = 'ویکیپیڈیا:درخواست منتقلی زمرہ جات'
limit = 3000
vBot = CatMoveInput(cacheFile, project, limit)
req = vBot.processInput(page_name)
for task in req['tasks']:
mBot = CatMoveBot(page_name, project)
mBot.run(task, req['user'])
if __name__ == '__main__':
main()