jenkins-bot has submitted this change and it was merged.
Change subject: Add Wikidata support to isbn.py script
......................................................................
Add Wikidata support to isbn.py script
The support is done via a separate Bot class. It can find ISBN-10
and ISBN-13 property IDs or they can be provided manually by the
user.
Also, the patch adds support for -always option in WikidataBot
when using new method WikidataBot.userEditEntity.
Bug: T85242
Change-Id: I38cf459d78eb02102da6a169c9c0633ee95b1f3b
---
M pywikibot/bot.py
M scripts/isbn.py
M tests/isbn_tests.py
3 files changed, 259 insertions(+), 12 deletions(-)
Approvals:
John Vandenberg: Looks good to me, approved
jenkins-bot: Verified
diff --git a/pywikibot/bot.py b/pywikibot/bot.py
index d56b526..0bb79ba 100644
--- a/pywikibot/bot.py
+++ b/pywikibot/bot.py
@@ -1006,19 +1006,36 @@
if 'comment' in kwargs:
pywikibot.output(u'Comment: %s' % kwargs['comment'])
+ page.text = newtext
+ self._save_page(page, page.save, **kwargs)
+
+ def _save_page(self, page, func, *args, **kwargs):
+ """
+ Helper function to handle page save-related option error handling.
+
+ @param page: currently edited page
+ @param func: the function to call
+ @param args: passed to the function
+ @param kwargs: passed to the function
+ @kwarg ignore_server_errors: if True, server errors will be reported
+ and ignored (default: False)
+ @kwtype ignore_server_errors: bool
+ @kwarg ignore_save_related_errors: if True, errors related to
+ page save will be reported and ignored (default: False)
+ @kwtype ignore_save_related_errors: bool
+ """
if not self.user_confirm('Do you want to accept these changes?'):
return
if 'async' not in kwargs and self.getOption('always'):
kwargs['async'] = True
- page.text = newtext
-
- ignore_save_related_errors = kwargs.pop('ignore_save_related_errors',
False)
+ ignore_save_related_errors = kwargs.pop('ignore_save_related_errors',
+ False)
ignore_server_errors = kwargs.pop('ignore_server_errors', False)
try:
- page.save(**kwargs)
+ func(*args, **kwargs)
except pywikibot.PageSaveRelatedError as e:
if not ignore_save_related_errors:
raise
@@ -1164,6 +1181,67 @@
self.source_values[family_code][source_lang] =
pywikibot.ItemPage(self.repo,
family[source_lang])
+ def get_property_by_name(self, property_name):
+ """
+ Find given property and return its ID.
+
+ Method first uses site.search() and if the property isn't found, then
+ asks user to provide the property ID.
+
+ @param property_name: property to find
+ @type property_name: str
+ """
+ ns = self.site.data_repository().property_namespace
+ for page in self.site.search(property_name, step=1, total=1,
+ namespaces=ns):
+ page = pywikibot.PropertyPage(self.site.data_repository(),
+ page.title())
+ pywikibot.output(u"Assuming that %s property is %s." %
+ (property_name, page.id))
+ return page.id
+ return pywikibot.input(u'Property %s was not found. Please enter the '
+ u'property ID (e.g. P123) of it:'
+ % property_name).upper()
+
+ def user_edit_entity(self, item, data=None, **kwargs):
+ """
+ Edit entity with data provided, with user confirmation as required.
+
+ @param item: page to be edited
+ @type item: ItemPage
+ @param data: data to be saved, or None if the diff should be created
+ automatically
+ @kwarg summary: revision comment, passed to ItemPage.editEntity
+ @kwtype summary: str
+ @kwarg show_diff: show changes between oldtext and newtext (default:
+ True)
+ @kwtype show_diff: bool
+ @kwarg ignore_server_errors: if True, server errors will be reported
+ and ignored (default: False)
+ @kwtype ignore_server_errors: bool
+ @kwarg ignore_save_related_errors: if True, errors related to
+ page save will be reported and ignored (default: False)
+ @kwtype ignore_save_related_errors: bool
+ """
+ self.current_page = item
+
+ show_diff = kwargs.pop('show_diff', True)
+ if show_diff:
+ if data is None:
+ diff = item.toJSON(diffto=(
+ item._content if hasattr(item, '_content') else None))
+ else:
+ diff = pywikibot.WikibasePage._normalizeData(data)
+ pywikibot.output(json.dumps(diff, indent=4, sort_keys=True))
+
+ if 'summary' in kwargs:
+ pywikibot.output(u'Change summary: %s' % kwargs['summary'])
+
+ # TODO async in editEntity should actually have some effect (bug
T86074)
+ # TODO PageSaveRelatedErrors should be actually raised in editEntity
+ # (bug T86083)
+ self._save_page(item, item.editEntity, data, **kwargs)
+
def getSource(self, site):
"""
Create a Claim usable as a source for Wikibase statements.
diff --git a/scripts/isbn.py b/scripts/isbn.py
index 54b2eb5..412214e 100755
--- a/scripts/isbn.py
+++ b/scripts/isbn.py
@@ -27,6 +27,13 @@
-always Don't prompt you for each replacement.
+-prop-isbn-10 Sets ISBN-10 property ID, so it's not tried to be found
+ automatically.
+ The usage is as follows: -prop-isbn-10:propid
+
+-prop-isbn-13 Sets ISBN-13 property ID. The format and purpose is the
+ same as in -prop-isbn-10.
+
"""
#
# (C) Pywikibot team, 2009-2014
@@ -38,7 +45,7 @@
import re
import pywikibot
-from pywikibot import i18n, pagegenerators, Bot
+from pywikibot import i18n, pagegenerators, Bot, WikidataBot
docuReplacements = {
'¶ms;': pagegenerators.parameterHelp,
@@ -1415,6 +1422,89 @@
self.treat(page)
+class IsbnWikibaseBot(WikidataBot):
+
+ """ISBN bot to be run on Wikibase sites."""
+
+ def __init__(self, generator, **kwargs):
+ self.availableOptions.update({
+ 'to13': False,
+ 'format': False,
+ })
+ self.isbn_10_prop_id = kwargs.pop('prop-isbn-10', None)
+ self.isbn_13_prop_id = kwargs.pop('prop-isbn-13', None)
+
+ super(IsbnWikibaseBot, self).__init__(use_from_page=None, **kwargs)
+
+ self.generator = generator
+ if self.isbn_10_prop_id is None:
+ self.isbn_10_prop_id = self.get_property_by_name('ISBN-10')
+ if self.isbn_13_prop_id is None:
+ self.isbn_13_prop_id = self.get_property_by_name('ISBN-13')
+ self.comment = i18n.twtranslate(pywikibot.Site(), 'isbn-formatting')
+
+ def treat(self, page, item):
+ change_messages = []
+
+ if self.isbn_10_prop_id in item.claims:
+ for claim in item.claims[self.isbn_10_prop_id]:
+ try:
+ isbn = getIsbn(claim.getTarget())
+ except InvalidIsbnException as e:
+ pywikibot.output(e)
+ continue
+
+ old_code = claim.getTarget()
+
+ if self.getOption('format'):
+ isbn.format()
+
+ if self.getOption('to13'):
+ isbn = isbn.toISBN13()
+
+ item.claims[claim.getID()].remove(claim)
+ claim = pywikibot.Claim(self.repo, self.isbn_13_prop_id)
+ claim.setTarget(isbn.code)
+ if self.isbn_13_prop_id in item.claims:
+ item.claims[self.isbn_13_prop_id].append(claim)
+ else:
+ item.claims[self.isbn_13_prop_id] = [claim]
+ change_messages.append('Changing %s (%s) to %s (%s)' %
+ (self.isbn_10_prop_id, old_code,
+ self.isbn_13_prop_id, isbn.code))
+ continue
+
+ if old_code == isbn.code:
+ continue
+ claim.setTarget(isbn.code)
+ change_messages.append('Changing %s (%s --> %s)' %
+ (self.isbn_10_prop_id, old_code,
+ isbn.code))
+
+ # -format is the only option that has any effect on ISBN13
+ if self.getOption('format') and self.isbn_13_prop_id in item.claims:
+ for claim in item.claims[self.isbn_13_prop_id]:
+ try:
+ isbn = getIsbn(claim.getTarget())
+ except InvalidIsbnException as e:
+ pywikibot.output(e)
+ continue
+
+ old_code = claim.getTarget()
+ isbn.format()
+ if old_code == isbn.code:
+ continue
+ change_messages.append(
+ 'Changing %s (%s --> %s)' % (self.isbn_13_prop_id,
+ claim.getTarget(), isbn.code))
+ claim.setTarget(isbn.code)
+
+ if change_messages:
+ self.current_page = item
+ pywikibot.output('\n'.join(change_messages))
+ self.user_edit_entity(item, summary=self.comment)
+
+
def main(*args):
"""
Process command line arguments and invoke bot.
@@ -1430,8 +1520,20 @@
local_args = pywikibot.handle_args(args)
genFactory = pagegenerators.GeneratorFactory()
+ # Check whether we're running on Wikibase site or not
+ # FIXME: See T85483 and run() in WikidataBot
+ site = pywikibot.Site()
+ data_site = site.data_repository()
+ use_wikibase = (data_site is not None and
+ data_site.family == site.family and
+ data_site.code == site.code)
+
for arg in local_args:
- if arg.startswith('-') and arg[1:] in ('always', 'to13', 'format'):
+ if arg.startswith('-prop-isbn-10:'):
+ options[arg[1:len('-prop-isbn-10')]] = arg[len('-prop-isbn-10:'):]
+ elif arg.startswith('-prop-isbn-13:'):
+ options[arg[1:len('-prop-isbn-13')]] = arg[len('-prop-isbn-13:'):]
+ elif arg.startswith('-') and arg[1:] in ('always', 'to13', 'format'):
options[arg[1:]] = True
else:
genFactory.handleArg(arg)
@@ -1439,7 +1541,10 @@
gen = genFactory.getCombinedGenerator()
if gen:
preloadingGen = pagegenerators.PreloadingGenerator(gen)
- bot = IsbnBot(preloadingGen, **options)
+ if use_wikibase:
+ bot = IsbnWikibaseBot(preloadingGen, **options)
+ else:
+ bot = IsbnBot(preloadingGen, **options)
bot.run()
else:
pywikibot.showHelp()
diff --git a/tests/isbn_tests.py b/tests/isbn_tests.py
index cd2c513..26ec753 100644
--- a/tests/isbn_tests.py
+++ b/tests/isbn_tests.py
@@ -9,10 +9,15 @@
__version__ = '$Id$'
-from scripts.isbn import ISBN10, ISBN13, InvalidIsbnException as IsbnExc, \
- getIsbn, hyphenateIsbnNumbers, convertIsbn10toIsbn13, main
-from tests.aspects import TestCase, unittest
-from pywikibot import Bot
+from scripts.isbn import (
+ ISBN10, ISBN13, InvalidIsbnException as IsbnExc,
+ getIsbn, hyphenateIsbnNumbers, convertIsbn10toIsbn13,
+ main
+)
+from tests.aspects import (
+ unittest, TestCase, WikibaseTestCase, ScriptMainTestCase
+)
+from pywikibot import Bot, Claim, ItemPage
class TestIsbn(TestCase):
@@ -89,7 +94,7 @@
isbn.format)
-class TestIsbnBot(TestCase):
+class TestIsbnBot(ScriptMainTestCase):
"""Test isbnbot with non-write patching (if the testpage exists)."""
@@ -123,5 +128,64 @@
TestIsbnBot.newtext = newtext
+class TestIsbnWikibaseBot(ScriptMainTestCase, WikibaseTestCase):
+
+ """Test isbnbot on Wikibase site with non-write patching."""
+
+ family = 'wikidata'
+ code = 'test'
+
+ @classmethod
+ def setUpClass(cls):
+ super(TestIsbnWikibaseBot, cls).setUpClass()
+
+ # Check if the unit test item page and the property both exist
+ item_ns = cls.get_repo().item_namespace
+ for page in cls.get_site().search('IsbnWikibaseBotUnitTest', step=1,
+ total=1, namespaces=item_ns):
+ cls.test_page_qid = page.title()
+ item_page = ItemPage(cls.get_repo(), page.title())
+ for pid, claims in item_page.get()['claims'].items():
+ for claim in claims:
+ prop_page = pywikibot.PropertyPage(cls.get_repo(),
+ claim.getID())
+ prop_page.get()
+ if ('ISBN-10' in prop_page.labels.values() and
+ claim.getTarget() == '097522980x'):
+ return
+ raise unittest.SkipTest(
+ u'%s: "ISBN-10" property was not found in '
+ u'"IsbnWikibaseBotUnitTest" item page' % cls.__name__)
+ raise unittest.SkipTest(
+ u'%s: "IsbnWikibaseBotUnitTest" item page was not found'
+ % cls.__name__)
+
+ def setUp(self):
+ TestIsbnWikibaseBot._original_setTarget = Claim.setTarget
+ Claim.setTarget = setTarget_dummy
+ TestIsbnWikibaseBot._original_editEntity = ItemPage.editEntity
+ ItemPage.editEntity = editEntity_dummy
+ super(TestIsbnWikibaseBot, self).setUp()
+
+ def tearDown(self):
+ Claim.setTarget = TestIsbnWikibaseBot._original_setTarget
+ ItemPage.editEntity = TestIsbnWikibaseBot._original_editEntity
+ super(TestIsbnWikibaseBot, self).tearDown()
+
+ def test_isbn(self):
+ main('-page:' + self.test_page_qid, '-always', '-format')
+ self.assertEqual(self.setTarget_value, '0-9752298-0-X')
+ main('-page:' + self.test_page_qid, '-always', '-to13')
+ self.assertTrue(self.setTarget_value, '978-0975229804')
+
+
+def setTarget_dummy(self, value):
+ TestIsbnWikibaseBot.setTarget_value = value
+ TestIsbnWikibaseBot._original_setTarget(self, value)
+
+
+def editEntity_dummy(self, data=None, **kwargs):
+ pass
+
if __name__ == "__main__":
unittest.main()
--
To view, visit https://gerrit.wikimedia.org/r/182989
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: I38cf459d78eb02102da6a169c9c0633ee95b1f3b
Gerrit-PatchSet: 17
Gerrit-Project: pywikibot/core
Gerrit-Branch: master
Gerrit-Owner: M4tx <[email protected]>
Gerrit-Reviewer: John Vandenberg <[email protected]>
Gerrit-Reviewer: Ladsgroup <[email protected]>
Gerrit-Reviewer: M4tx <[email protected]>
Gerrit-Reviewer: Merlijn van Deen <[email protected]>
Gerrit-Reviewer: Ricordisamoa <[email protected]>
Gerrit-Reviewer: XZise <[email protected]>
Gerrit-Reviewer: jenkins-bot <>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits