jenkins-bot has submitted this change. ( https://gerrit.wikimedia.org/r/c/pywikibot/core/+/637872 )
Change subject: [IMPR] add support for some 'wbset' actions in DataSite ......................................................................
[IMPR] add support for some 'wbset' actions in DataSite
It can be useful for atomic operations as the API add automatic summary, which creates a cleaner page history.
To be decided: add support also at page level or not, given that method for supporting such actions are present via editEntity.
Change-Id: Idfd5de485d2b5aab8d8b1960ceb3c3a3c6b64315 --- M pywikibot/site/__init__.py M tests/wikibase_edit_tests.py 2 files changed, 199 insertions(+), 0 deletions(-)
Approvals: Xqt: Looks good to me, approved jenkins-bot: Verified
diff --git a/pywikibot/site/__init__.py b/pywikibot/site/__init__.py index c21feb2..f33c42e 100644 --- a/pywikibot/site/__init__.py +++ b/pywikibot/site/__init__.py @@ -7637,6 +7637,146 @@ total=total, parameters=parameters) return gen
+ def _wbset_action(self, itemdef, action, action_data, **kwargs): + """ + Execute wbset{action}' on a Wikibase item. + + Supported actions are: + wbsetaliases, wbsetdescription, wbsetlabel and wbsetsitelink + + @param itemdef: Item to modify or create + @type itemdef: str, WikibasePage or Page coonected to such item + @param action: wbset{action] to perform: + 'wbsetaliases', 'wbsetdescription', 'wbsetlabel', 'wbsetsitelink' + @type action: str + @param data: data to be used in API request, see API help + @type data: SiteLink or dict + wbsetaliases: + dict shall have the following structure: + {'language': value (str), + 'add': list of language codes (str), + 'remove': list of language codes (str), + 'set' list of language codes (str) + } + 'add' and 'remove' are alternative to 'set' + wbsetdescription and wbsetlabel: + dict shall have keys 'language', 'value' + wbsetsitelink: + dict shall have keys 'linksite', 'linktitle' and + optionally 'badges' + @kwargs bot: Whether to mark the edit as a bot edit, default is False + @type bot: bool + @return: query result + @rtype: dict + @raises AssertionError, TypeError + """ + def format_sitelink(sitelink): + """Convert SiteLink to a dict accepted by wbsetsitelink API.""" + if isinstance(sitelink, pywikibot.page.SiteLink): + _dict = { + 'linksite': sitelink._sitekey, + 'linktitle': sitelink._rawtitle, + 'badges': '|'.join([b.title() for b in sitelink.badges]), + } + else: + _dict = sitelink + + return _dict + + def prepare_data(action, data): + """Prepare data as expected by API.""" + if action == 'wbsetaliases': + res = data + keys = set(res) + assert keys < {'language', 'add', 'remove', 'set'} + assert keys & {'add', 'set'} == {} + assert keys & {'remove', 'set'} == {} + elif action in ('wbsetlabel', 'wbsetdescription'): + res = data + keys = set(res) + assert keys == {'language', 'value'} + elif action == 'wbsetsitelink': + res = format_sitelink(data) + keys = set(res) + assert keys >= {'linksite'} + assert keys <= {'linksite', 'linktitle', 'badges'} + else: + raise ValueError('Something has gone wrong ...') + + return res + + # Supported actions + assert action in ('wbsetaliases', 'wbsetdescription', + 'wbsetlabel', 'wbsetsitelink'), \ + 'action {} not supported.'.format(action) + + # prefer ID over (site, title) + if isinstance(itemdef, str): + itemdef = pywikibot.ItemPage(self, itemdef) + elif isinstance(itemdef, pywikibot.Page): + try: + itemdef = itemdef.data_item() + except pywikibot.NoPage: + itemdef = pywikibot.ItemPage(self) + if not isinstance(itemdef, pywikibot.page.WikibasePage): + raise TypeError('itemdef shall be str, WikibasePage or Page') + + params = itemdef._defined_by(singular=True) + # TODO: support 'new' + baserevid = kwargs.pop('baserevid', 0) or itemdef.latest_revision_id + params.update( + {'id': itemdef.id, + 'baserevid': baserevid, + 'action': action, + 'token': self.tokens['edit'], + 'bot': kwargs.pop('bot', False), + }) + params.update(prepare_data(action, action_data)) + + for arg in kwargs: + if arg in ['summary']: + params[arg] = kwargs[arg] + else: + warn('Unknown parameter {} for action {}, ignored' + .format(arg, action), UserWarning, 2) + + req = self._simple_request(**params) + data = req.submit() + return data + + def wbsetaliases(self, itemdef, aliases, **kwargs): + """ + Set aliases for a single Wikibase entity. + + See self._wbset_action(self, itemdef, action, action_data, **kwargs) + """ + return self._wbset_action(itemdef, 'wbsetaliases', aliases, **kwargs) + + def wbsetdescription(self, itemdef, description, **kwargs): + """ + Set description for a single Wikibase entity. + + See self._wbset_action(self, itemdef, action, action_data, **kwargs) + """ + return self._wbset_action(itemdef, 'wbsetdescription', description, + **kwargs) + + def wbsetlabel(self, itemdef, label, **kwargs): + """ + Set label for a single Wikibase entity. + + See self._wbset_action(self, itemdef, action, action_data, **kwargs) + """ + return self._wbset_action(itemdef, 'wbsetlabel', label, **kwargs) + + def wbsetsitelink(self, itemdef, sitelink, **kwargs): + """ + Set, remove or modify a sitelink on a Wikibase item. + + See self._wbset_action(self, itemdef, action, action_data, **kwargs) + """ + return self._wbset_action(itemdef, 'wbsetsitelink', sitelink, **kwargs) +
wrapper = ModuleDeprecationWrapper(__name__) # Note: use LoginStatus instead of _LoginStatus diff --git a/tests/wikibase_edit_tests.py b/tests/wikibase_edit_tests.py index e8ece10..a75fd81 100644 --- a/tests/wikibase_edit_tests.py +++ b/tests/wikibase_edit_tests.py @@ -458,6 +458,65 @@ self.assertNotIn('P88', claim.qualifiers.keys())
+class TestWikibaseDataSiteWbsetActions(WikibaseTestCase): + + """Run general wikibase write tests.""" + + family = 'wikidata' + code = 'test' + + user = True + write = True + + def setUp(self): + """Setup tests.""" + self.testsite = self.get_repo() + self.item = pywikibot.ItemPage(self.testsite, 'Q68') + badge = pywikibot.ItemPage(self.testsite, 'Q608') + self.sitelink = pywikibot.page.SiteLink('Test page', + site='enwikisource', + badges=[badge]) + super().setUp() + + def tearDown(self): + """Tear down tests.""" + self.item = None + self.sitelink = None + super().tearDown() + + def test_wbsetlabel_set_from_id(self): + """Test setting an Italian label using id.""" + self.assertEqual(self.item.getID(), 'Q68') + self.testsite.wbsetlabel('Q68', {'language': 'it', 'value': 'Test123'}) + self.item.get(force=True) + self.assertEqual(self.item.labels['it'], 'Test123') + + def test_wbsetlabel_remove_from_item(self): + """Test removing an Italian label using item.""" + self.assertEqual(self.item.getID(), 'Q68') + self.testsite.wbsetlabel(self.item, {'language': 'it', 'value': ''}) + # Check 'it' label is removed + self.item.get(force=True) + self.assertNotIn('it', self.item.labels.keys()) + + def test_wbsetsitelink_set_remove(self): + """Test setting a sitelink using id.""" + self.assertEqual(self.item.getID(), 'Q68') + # add sitelink + self.testsite.wbsetsitelink( + 'Q68', + {'linksite': 'enwikisource', + 'linktitle': 'Test page', + 'badges': 'Q608' + }) + self.item.get(force=True) + self.assertEqual(self.item.sitelinks['enwikisource'], self.sitelink) + # remove sitelink + self.testsite.wbsetsitelink(self.item, {'linksite': 'enwikisource'}) + self.item.get(force=True) + self.assertIsNone(self.item.sitelinks.get('enwikisource')) + + if __name__ == '__main__': # pragma: no cover with suppress(SystemExit): unittest.main()