Jonathan Golder
7 years ago
7 changed files with 920 additions and 460 deletions
@ -0,0 +1,281 @@ |
|||
#!/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) |
|||
|
|||
def run(self): |
|||
""" |
|||
Controls the overal parsing process, using super class for page switch |
|||
|
|||
Needed to do things before/after treating pages is done |
|||
""" |
|||
try: |
|||
|
|||
super( MarkPagesBot, self ).run() |
|||
|
|||
except: |
|||
raise |
|||
|
|||
else: |
|||
# Do status redfam status updates |
|||
for redfam in self.redfams: |
|||
redfam.update_status() |
|||
|
|||
RedFamWorker.flush_db_cache() |
|||
|
|||
@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 = list( 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, summary=summary ) |
|||
|
|||
# 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) |
@ -1 +1 @@ |
|||
Subproject commit 28d03f35b848a33ad45d3f5f8f3f82e8c45534ec |
|||
Subproject commit 49ada2993e345600523c161c5e2516ec65625684 |
Loading…
Reference in new issue