Source code for scripts.movepages

#!/usr/bin/env python3
"""This script can move pages.

These command line parameters can be used to specify which pages to work
on:

&params;

Furthermore, the following command line parameters are supported:

-from           The page to move from.

-to             The page to move to.

-noredirect     Leave no redirect behind.

-notalkpage     Do not move this page's talk page (if it exists)

-nosubpages     Do not move subpages

-prefix         Move pages by adding a namespace prefix to the names of
                the pages. (Will remove the old namespace prefix if any)
                Argument can also be given as ``-prefix:namespace:``.

-always         Don't prompt to make changes, just do them.

-skipredirects  Skip redirect pages (Warning: increases server load)

-summary        [str] Prompt for a custom summary, bypassing the
                predefined message texts. Argument can also be given as
                ``-summary:XYZ``.

-pairsfile      Read pairs of file names from a file. The file must be
                in a format [[frompage]] [[topage]] [[frompage]]
                [[topage]] ... Argument can also be given as
                ``-pairsfile:filename``
"""
#
# (C) Pywikibot team, 2006-2024
#
# Distributed under the terms of the MIT license.
#
from __future__ import annotations

import re
from functools import partial
from itertools import zip_longest

import pywikibot
from pywikibot import i18n, pagegenerators
from pywikibot.bot import CurrentPageBot
from pywikibot.exceptions import PageRelatedError


# This is required for the text that is shown when you run this script
# with the parameter -help.
docuReplacements = {'&params;': pagegenerators.parameterHelp}  # noqa: N816


[docs] class MovePagesBot(CurrentPageBot): """Page move bot. .. versionchanged:: 7.2 `movesubpages` option was added """ update_options = { 'prefix': '', 'noredirect': False, 'movetalkpage': True, 'movesubpages': True, 'skipredirects': False, 'summary': '', } def __init__(self, **kwargs) -> None: """Initializer.""" super().__init__(**kwargs) self.create_title = None
[docs] def move_one(self, page, new_page_tite) -> None: """Move one page to new_page_tite.""" msg = self.opt.summary if not msg: msg = i18n.twtranslate(page.site, 'movepages-moving') pywikibot.info(f'Moving page {page} to [[{new_page_tite}]]') try: page.move(new_page_tite, reason=msg, movetalk=self.opt.movetalkpage, movesubpages=self.opt.movesubpages, noredirect=self.opt.noredirect) except PageRelatedError as e: pywikibot.error(e)
[docs] def skip_page(self, page): """Treat only non-redirect pages if 'skipredirects' is set.""" if self.opt.skipredirects and page.isRedirectPage(): pywikibot.warning( f'Page {page} on {page.site} is a redirect; skipping' ) return True return super().skip_page(page)
@staticmethod def _select_action(page): """Manage interactive choices.""" def create_new_title_change(page, new_title=None): """Change helper function.""" if new_title is None: return pywikibot.input('New page name:') return new_title def create_new_title_append(start, end, page, namespace=None): """Append helper function.""" page_title = page.title(with_ns=False) new_page_tite = f'{start}{page_title}{end}' if namespace is not None: new_page_tite = f'{namespace}:{new_page_tite}' return new_page_tite def create_new_title_regex(regex, replacement, page, namespace=None): """Replace helper function.""" page_title = page.title(with_ns=False) new_page_title = regex.sub(replacement, page_title) if namespace is not None: new_page_title = f'{namespace}:{new_page_title}' return new_page_title def manage_namespace(page): """Manage interactive choices for namespace prefix.""" namespace = page.site.namespace(page.namespace()) q = pywikibot.input_yn('Do you want to remove the ' f'namespace prefix "{namespace}:"?', automatic_quit=False) return None if q else namespace choice = pywikibot.input_choice('What do you want to do?', [('change page name', 'c'), ('append to page name', 'a'), ('use a regular expression', 'r'), ('next page', 'n')]) if choice == 'c': handler = partial(create_new_title_change, new_title=create_new_title_change(page)) choices = [('yes', 'y'), ('no', 'n')] elif choice == 'a': start = pywikibot.input('Append this to the start:') end = pywikibot.input('Append this to the end:') ns = manage_namespace(page) handler = partial(create_new_title_append, start, end, namespace=ns) choices = [('yes', 'y'), ('no', 'n'), ('all', 'a')] elif choice == 'r': search_pattern = pywikibot.input('Enter the search pattern:') regex = re.compile(search_pattern) replacement = pywikibot.input('Enter the replace pattern:') ns = manage_namespace(page) handler = partial(create_new_title_regex, regex, replacement, namespace=ns) choices = [('yes', 'y'), ('no', 'n'), ('all', 'a')] else: handler = None choices = [] return handler, choices def _title_creator(self, page, prefix): """Create function to generate new title.""" def create_new_title_prefix(prefix, page): """Replace prefix helper function.""" page_title = page.title(with_ns=False) return f'{prefix}{page_title}' if prefix: handler = partial(create_new_title_prefix, prefix) choices = [('yes', 'y'), ('no', 'n'), ('all', 'a')] else: handler, choices = self._select_action(page) return choices, handler
[docs] def treat_page(self) -> None: """Treat a single page.""" page = self.current_page if not self.opt.always: choices, create_title = self._title_creator(page, self.opt.prefix) if create_title is None: return self.create_title = create_title choice = pywikibot.input_choice( f'Change the page title to {create_title(page)!r}?', choices) if choice == 'y': pass elif choice == 'a': self.opt.always = True else: return new_page_title = self.create_title(page) self.move_one(page, new_page_title)
[docs] def main(*args: str) -> None: """Process command line arguments and invoke bot. If args is an empty list, sys.argv is used. :param args: command line arguments """ old_name = None options = {} from_to_pairs = [] # Process global args and prepare generator args parser local_args = pywikibot.handle_args(args) gen_factory = pagegenerators.GeneratorFactory() local_args = gen_factory.handle_args(local_args) for arg in local_args: opt, _, value = arg.partition(':') if not opt.startswith('-'): continue opt = opt[1:] if opt == 'pairsfile': filename = value or pywikibot.input( 'Enter the name of the file containing pairs:') page_gen = [pagegenerators.TextIOPageGenerator(filename)] * 2 for old_page, new_page in zip_longest(*page_gen, fillvalue=None): if new_page is None: pywikibot.warning( f'file {filename} contains odd number of links') else: from_to_pairs.append([old_page.title(), new_page.title()]) elif opt in ('always', 'noredirect', 'skipredirects'): options[opt] = True elif opt in ('notalkpage', 'nosubpages'): options[opt.replace('no', 'move', 1)] = False elif opt == 'from': if old_name: pywikibot.warning(f'-from:{old_name} without -to:') old_name = value elif opt == 'to': if old_name: from_to_pairs.append([old_name, value]) old_name = None else: pywikibot.warning(f'{arg} without -from') elif opt == 'prefix': options[opt] = value or pywikibot.input('Enter the prefix:') elif opt == 'summary': options[opt] = value or pywikibot.input('Enter the summary:') if old_name: pywikibot.warning(f'-from:{old_name} without -to:') site = pywikibot.Site() if not site.logged_in(): site.login() bot = MovePagesBot(**options) for old_title, new_title in from_to_pairs: bot.move_one(pywikibot.Page(site, old_title), new_title) gen = gen_factory.getCombinedGenerator(preload=True) if gen: bot = MovePagesBot(generator=gen, **options) bot.run() elif not from_to_pairs: pywikibot.bot.suggest_help(missing_generator=True)
if __name__ == '__main__': main()