User:Surjection/also-update.py
import wikitextlib
import pywikibot
from pywikibot import Site, Page, Category
import sys
import collections
import functools
import queue
import argparse
ALSO_TEMPLATES = {"also", "see also", "also see", "seealso", "xsee", "xalso", "See also"}
ALSO_TEMPLATES_PREFERRED = "also"
PLACE_ALSO_BENEATH_CHARACTER_INFOS = False
DEBUG = False
enwikt = Site("en", fam="wiktionary")
enwikt.login()
@functools.cache
def resolve_page(title):
return Page(enwikt, title)
def save_page(title, text, summary):
page = resolve_page(title)
page.text = text
page.save(summary=summary, minor=False)
def should_visit(page):
return page.exists() and page.namespace().id == 0
def get_also_template(page):
find_from = ""
for section in wikitextlib.iterate_sections(page.text):
if section.heading.level == 2 and section.heading.text is None:
find_from = section.text
break
for template in wikitextlib.find_templates(find_from):
if template.name in ALSO_TEMPLATES:
return wikitextlib.get_positional_args(template)[1:]
if template.name.startswith("also/"):
return ...
return None
def make_also_template(name, variants, extra_args):
args = collections.OrderedDict([(i + 1, variant) for i, variant in enumerate(variants)])
if extra_args:
for key, value in extra_args.items():
if type(key) != int:
args[key] = value
return wikitextlib.make_template(name, args)
def update_also_template_parameters(template, page, variants, variation_pages):
recognized = set(variants + variation_pages)
recognized.add(page.title())
present_args = wikitextlib.get_positional_args(template)[1:]
unrecognized_variants = [variant for variant in present_args if variant not in recognized]
named_args = collections.OrderedDict([(key, value) for key, value in template.args.items() if type(key) != int])
return make_also_template(template.name, variants + unrecognized_variants + variation_pages, named_args)
def update_also_template(page, variants, variation_pages, edit_summary):
has_template = get_also_template(page) is not None
result = ""
if not page.exists():
# never create a new page
return
if has_template:
has_replaced = False
def replacement(template):
nonlocal has_replaced
if has_replaced:
return ...
has_replaced = True
return update_also_template_parameters(template, page, variants, variation_pages)
result = wikitextlib.replace_templates_if(page.text,
lambda template: template.name in ALSO_TEMPLATES,
replacement)
else:
assert ALSO_TEMPLATES_PREFERRED in ALSO_TEMPLATES
also = make_also_template(ALSO_TEMPLATES_PREFERRED, variants + variation_pages, None)
if PLACE_ALSO_BENEATH_CHARACTER_INFOS:
lines = page.text.splitlines()
character_infos = 0
while character_infos < len(lines):
line = lines[character_infos]
has_character_info = False
for template in wikitextlib.find_templates(line):
if template.name in {"character info", "character info/subpage", "character info/save memory", "character info/var"}:
has_character_info = True
break
if not has_character_info:
break
character_infos += 1
result = "\n".join(lines[:character_infos] + [also] + lines[character_infos:])
else:
result = also + "\n" + page.text
if page.text == result:
return
if DEBUG:
print("Saving", page.title(), edit_summary)
pywikibot.showDiff(page.text, result)
else:
page.text = result
page.save(summary = edit_summary, minor = False)
def process_tree(root, edit_summary, add_links):
variants = collections.OrderedDict([(root.title(), root)])
visit_queue = queue.SimpleQueue()
edit_queue = [root]
variation_pages = []
do_not_update = set()
visit_queue.put(root)
while not visit_queue.empty():
page = visit_queue.get()
if DEBUG:
print("Visiting", page.title())
if page is root and add_links:
for candidate_name in add_links:
target = resolve_page(candidate_name)
variants[candidate_name] = target
visit_queue.put(target)
edit_queue.append(target)
add_links = set()
also = get_also_template(page)
if also is ...:
do_not_update.add(page.title())
elif also is not None:
for candidate_name in also:
if "Appendix:Variations " in candidate_name:
variation_pages.append(candidate_name)
elif candidate_name not in variants:
target = resolve_page(candidate_name)
variants[candidate_name] = target
visit_queue.put(target)
edit_queue.append(target)
updated = set()
for page in edit_queue:
page_name = page.title()
if page_name in do_not_update:
continue
variants_for_page = [variant_name for variant_name in variants.keys() if variant_name != page_name]
updated.add(page_name)
if variants_for_page:
update_also_template(page, variants_for_page, variation_pages, edit_summary)
return updated
def update_also(page_name, add_links):
page = resolve_page(page_name)
if should_visit(page):
return process_tree(page, "(bot) update [[Template:also]] references from [[{}]]".format(page_name), add_links)
else:
return set()
if __name__ == "__main__":
updated = set()
parser = argparse.ArgumentParser()
parser.add_argument('page', nargs='*')
parser.add_argument('-x', '--do', action='store_true', help='actually save edits')
parser.add_argument('-l', '--link', action='store_true', help='link all page parameters to each other')
args = parser.parse_args()
DEBUG = DEBUG or not args.do
for page_name in args.page:
if page_name not in updated:
updated.update(update_also(page_name, (set(args.page) - {page_name}) if args.link else set()))