You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

261 lines
9.3 KiB

#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# markpages.py
#
# Copyright 2016 GOLDERWEB – Jonathan Golder <jonathan@golderweb.de>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA 02110-1301, USA.
#
#
"""
Bot to mark pages which were/are subjects of redundance discussions
with templates
"""
from datetime import datetime
from pywikibot import pagegenerators
from pywikibot.bot import CurrentPageBot
import mwparserfromhell as mwparser
import jogobot
from lib.redfam import RedFamWorker
class MarkPagesBot( CurrentPageBot ): # sets 'current_page' on each treat()
"""
Bot class to mark pages which were/are subjects of redundance discussions
with templates
"""
def __init__( self, genFactory, **kwargs ):
"""
Constructor
Parameters:
@param genFactory GenFactory with parsed pagegenerator args to
build generator
@type genFactory pagegenerators.GeneratorFactory
@param **kwargs Additional args
@type iterable
"""
# Init attribute
self.__redfams = None # Will hold a generator with our redfams
# We do not use predefined genFactory as there is no sensefull case to
# give a generator via cmd-line for this right now
self.genFactory = pagegenerators.GeneratorFactory()
# Build generator with genFactory
self.build_generator()
# Run super class init with builded generator
super( MarkPagesBot, self ).__init__(generator=self.gen)
@property
def redfams(self):
"""
Holds redfams generator to work on in this bot
"""
# Create generator if not present
if not self.__redfams:
end_after = datetime.strptime(
jogobot.config["red.markpages"]["mark_done_after"],
"%Y-%m-%d" )
self.__redfams = RedFamWorker.gen_by_status_and_ending(
"archived", end_after)
return self.__redfams
def build_generator( self ):
"""
Builds generator to pass to super class
"""
# Add Talkpages to work on to generatorFactory
self.genFactory.gens.append( self.redfam_talkpages_generator() )
# Set generator to pass to super class
self.gen = pagegenerators.PreloadingGenerator(
self.genFactory.getCombinedGenerator() )
def redfam_talkpages_generator( self ):
"""
Wrappers the redfam.article_generator and
passes it to pagegenerators.PageWithTalkPageGenerator().
Then it iterates over the generator and adds a reference to the
related redfam to each talkpage-object.
"""
for redfam in self.redfams:
# We need the talkpage (and only this) of each existing page
for talkpage in pagegenerators.PageWithTalkPageGenerator(
redfam.article_generator(
filter_existing=True,
exclude_article_status=["marked"] ),
return_talk_only=True ):
# Add reference to redfam to talkpages
talkpage.redfam = redfam
yield talkpage
def treat_page( self ):
"""
Handles work on current page
We get a reference to related redfam in current_page.redfam
"""
# First we need to have the current text of page
# and parse it as wikicode
self.current_wikicode = mwparser.parse( self.current_page.text )
# Add notice
# Returns True if added
# None if already present
add_ret = self.add_disc_notice_template()
# Convert wikicode back to string to save
self.new_text = str( self.current_wikicode )
# Define edit summary
summary = jogobot.config["red.markpages"]["mark_done_summary"].format(
reddisc=self.current_page.redfam.get_disc_link() ).strip()
# Make sure summary starts with "Bot:"
if not summary[:len("Bot:")] == "Bot:":
summary = "Bot: " + summary.strip()
# will return True if saved
# False if not saved because of errors
# None if change was not accepted by user
save_ret = self.put_current( self.new_text )
# Status
if add_ret is None or add_ret and save_ret:
self.current_page.redfam.article_add_status(
"marked",
title=self.current_page.title(withNamespace=False))
elif save_ret is None:
self.current_page.redfam.article_add_status(
"note_rej",
title=self.current_page.title(withNamespace=False))
else:
self.current_page.redfam.article_add_status(
"sav_err",
title=self.current_page.title(withNamespace=False))
def add_disc_notice_template( self ):
"""
Will take self.current_wikicode and adds disc notice template after the
last template in leading section or as first element if there is no
other template in leading section
"""
# The notice to add
self.disc_notice = \
self.current_page.redfam.generate_disc_notice_template()
# Check if it is already present in wikicode
if self.disc_notice_present():
return
# Find the right place to insert notice template
# Therfore we need the first section (if there is one)
leadsec = self.current_wikicode.get_sections(
flat=False, include_lead=True )[0]
# There is none on empty pages, so we need to check
if leadsec:
# Get the last template in leadsec
ltemplate = leadsec.filter_templates()[-1]
# If there is one, add notice after this
if ltemplate:
self.current_wikicode.insert_after(ltemplate, self.disc_notice)
# To have it in its own line we need to add a linbreak before
self.current_wikicode.insert_before(self.disc_notice, "\n" )
# If there is no template, add before first element on page
else:
self.current_wikicode.insert( 0, self.disc_notice )
# If there is no leadsec (and therefore no template in it, we will add
# before the first element
else:
self.current_wikicode.insert( 0, self.disc_notice )
# Notice was added
return True
def disc_notice_present(self):
"""
Checks if disc notice which shall be added is already present.
"""
# Iterate over Templates with same name (if any) to search equal
# Link to decide if they are the same
for present_notice in self.current_wikicode.ifilter_templates(
matches=self.disc_notice.name ):
# Get reddisc page.title of notice to add
add_notice_link_tile = self.disc_notice.get(
"Diskussion").partition("#")[0]
# Get reddisc page.title of possible present notice
present_notice_link_tile = present_notice.get(
"Diskussion").partition("#")[0]
# If those are equal, notice is already present
if add_notice_link_tile == present_notice_link_tile:
return True
# If nothing is found, loop will run till its end
else:
return False
# We need to overrite this since orginal from pywikibot.bot.CurrentPageBot
# does not return result of self._save_page
def put_current(self, new_text, ignore_save_related_errors=None,
ignore_server_errors=None, **kwargs):
"""
Call L{Bot.userPut} but use the current page.
It compares the new_text to the current page text.
@param new_text: The new text
@type new_text: basestring
@param ignore_save_related_errors: Ignore save related errors and
automatically print a message. If None uses this instances default.
@type ignore_save_related_errors: bool or None
@param ignore_server_errors: Ignore server errors and automatically
print a message. If None uses this instances default.
@type ignore_server_errors: bool or None
@param kwargs: Additional parameters directly given to L{Bot.userPut}.
@type kwargs: dict
"""
if ignore_save_related_errors is None:
ignore_save_related_errors = self.ignore_save_related_errors
if ignore_server_errors is None:
ignore_server_errors = self.ignore_server_errors
return self.userPut(
self.current_page, self.current_page.text, new_text,
ignore_save_related_errors=ignore_save_related_errors,
ignore_server_errors=ignore_server_errors,
**kwargs)