jenkins-bot merged this change.

View Change

Approvals: Framawiki: Looks good to me, approved jenkins-bot: Verified
[IMPR] Improvements for User class

new methods:
- User.logevents(): return user logged activities
- User.last_event: return the last user activity
(btw. the first is the creation of the account)
- User.first_edit: return the first edit of the given user
- User.last_edit: return the last edit of the given user

other changes:
- User.contributions(): enable all site.usercontribs options
(including reverse which might be missing), extend the doc string
- User.uploadedImages(): use User.logevents() here
- User.is_thankable: Simplify result
- site.logevents(): refer mediawiki api but don't list log types here;
some other doc changes

tests added.

Change-Id: I1aa05649b47eff990f63d38663ff647604e7e260
---
M pywikibot/page.py
M pywikibot/site.py
M tests/user_tests.py
3 files changed, 122 insertions(+), 22 deletions(-)

diff --git a/pywikibot/page.py b/pywikibot/page.py
index 6cff93b..373d339 100644
--- a/pywikibot/page.py
+++ b/pywikibot/page.py
@@ -3462,6 +3462,40 @@
"""
self.site.unblockuser(self, reason)

+ def logevents(self, **kwargs):
+ """Yield user activities.
+
+ @keyword logtype: only iterate entries of this type
+ (see mediawiki api documentation for available types)
+ @type logtype: basestring
+ @keyword page: only iterate entries affecting this page
+ @type page: Page or basestring
+ @keyword namespace: namespace to retrieve logevents from
+ @type namespace: int or Namespace
+ @keyword start: only iterate entries from and after this Timestamp
+ @type start: Timestamp or ISO date string
+ @keyword end: only iterate entries up to and through this Timestamp
+ @type end: Timestamp or ISO date string
+ @keyword reverse: if True, iterate oldest entries first
+ (default: newest)
+ @type reverse: bool
+ @keyword tag: only iterate entries tagged with this tag
+ @type tag: basestring
+ @keyword total: maximum number of events to iterate
+ @type total: int
+ @rtype: iterable
+ """
+ return self.site.logevents(user=self.username, **kwargs)
+
+ @property
+ def last_event(self):
+ """Return last user activity.
+
+ @return: last user log entry
+ @rtype: LogEntry
+ """
+ return next(iter(self.logevents(total=1)))
+
@deprecated("contributions")
@deprecate_arg("limit", "total") # To be consistent with rest of framework
def editedPages(self, total=500):
@@ -3479,7 +3513,7 @@
yield item[0]

@deprecated_args(limit='total', namespace='namespaces')
- def contributions(self, total=500, namespaces=[]):
+ def contributions(self, total=500, **kwargs):
"""
Yield tuples describing this user edits.

@@ -3490,16 +3524,47 @@

@param total: limit result to this number of pages
@type total: int
- @param namespaces: only iterate links in these namespaces
- @type namespaces: list
+ @keyword start: Iterate contributions starting at this Timestamp
+ @keyword end: Iterate contributions ending at this Timestamp
+ @keyword reverse: Iterate oldest contributions first (default: newest)
+ @keyword namespaces: only iterate pages in these namespaces
+ @type namespaces: iterable of basestring or Namespace key,
+ or a single instance of those types. May be a '|' separated
+ list of namespace identifiers.
+ @keyword showMinor: if True, iterate only minor edits; if False and
+ not None, iterate only non-minor edits (default: iterate both)
+ @keyword top_only: if True, iterate only edits which are the latest
+ revision (default: False)
+ @return tuple of pywikibot.Page, revid, pywikibot.Timestamp, comment
+ @rtype: tuple
"""
for contrib in self.site.usercontribs(
- user=self.username, namespaces=namespaces, total=total):
+ user=self.username, total=total, **kwargs):
ts = pywikibot.Timestamp.fromISOformat(contrib['timestamp'])
yield (Page(self.site, contrib['title'], contrib['ns']),
contrib['revid'],
ts,
contrib.get('comment'))
+
+ @property
+ def first_edit(self):
+ """Return first user contribution.
+
+ @return: first user contribution entry
+ @return tuple of pywikibot.Page, revid, pywikibot.Timestamp, comment
+ @rtype: tuple
+ """
+ return next(self.contributions(reverse=True, total=1))
+
+ @property
+ def last_edit(self):
+ """Return last user contribution.
+
+ @return: last user contribution entry
+ @return tuple of pywikibot.Page, revid, pywikibot.Timestamp, comment
+ @rtype: tuple
+ """
+ return next(self.contributions(total=1))

@deprecate_arg("number", "total")
def uploadedImages(self, total=10):
@@ -3515,8 +3580,8 @@
"""
if not self.isRegistered():
raise StopIteration
- for item in self.site.logevents(
- logtype='upload', user=self.username, total=total):
+ for item in self.logevents(
+ logtype='upload', total=total):
yield (item.page(),
unicode(item.timestamp()),
item.comment(),
@@ -3535,14 +3600,8 @@

@rtype: bool
"""
- if self.isAnonymous():
- return False
- if not self.isRegistered():
- return False
- if 'bot' in self.groups():
- return False
-
- return True
+ return not (self.isAnonymous() or self.isRegistered()
+ or 'bot' in self.groups())


class WikibasePage(BasePage):
diff --git a/pywikibot/site.py b/pywikibot/site.py
index 8accf26..cd7ff26 100644
--- a/pywikibot/site.py
+++ b/pywikibot/site.py
@@ -4511,10 +4511,8 @@
@note: logevents with logtype='block' only logs user blocks whereas
site.blocks iterates all blocks including IP ranges.

- @param logtype: only iterate entries of this type (see wiki
- documentation for available types, which will include "block",
- "protect", "rights", "delete", "upload", "move", "import",
- "patrol", "merge")
+ @param logtype: only iterate entries of this type
+ (see mediawiki api documentation for available types)
@type logtype: basestring
@param user: only iterate entries that match this user name
@type user: basestring
@@ -4775,8 +4773,11 @@
list of namespace identifiers.
@param showMinor: if True, iterate only minor edits; if False and
not None, iterate only non-minor edits (default: iterate both)
+ @param total: limit result to this number of pages
+ @type total: int
@param top_only: if True, iterate only edits which are the latest
- revision
+ revision (default: False)
+ @raises Error: either user or userprefix must be non-empty
@raises KeyError: a namespace identifier was not resolved
@raises TypeError: a namespace identifier has an inappropriate
type such as NoneType or bool
diff --git a/tests/user_tests.py b/tests/user_tests.py
index d604a97..f2a8b06 100644
--- a/tests/user_tests.py
+++ b/tests/user_tests.py
@@ -9,11 +9,11 @@

import pywikibot

+from pywikibot import Page, Timestamp, User
from pywikibot.exceptions import AutoblockUser
-from pywikibot.tools import suppress_warnings
-from pywikibot import User
+from pywikibot.tools import StringTypes, suppress_warnings

-from tests.aspects import TestCase, unittest
+from tests.aspects import DefaultSiteTestCase, TestCase, unittest


class TestUserClass(TestCase):
@@ -164,6 +164,46 @@
user.getUserTalkPage)


+class TestUserMethods(DefaultSiteTestCase):
+
+ """Test User methods with bot user."""
+
+ user = True
+
+ def test_contribution(self):
+ """Test the User.usercontribs() method."""
+ mysite = self.get_site()
+ user = User(mysite, mysite.user())
+ uc = list(user.contributions(total=10))
+ if not uc:
+ self.skipTest('User {0} has no contributions on site {1}.'
+ .format(mysite.user(), mysite))
+ self.assertLessEqual(len(uc), 10)
+ last = uc[0]
+ for contrib in uc:
+ self.assertIsInstance(contrib, tuple)
+ self.assertEqual(len(contrib), 4)
+ p, i, t, c = contrib
+ self.assertIsInstance(p, Page)
+ self.assertIsInstance(i, int)
+ self.assertIsInstance(t, Timestamp)
+ self.assertIsInstance(c, StringTypes)
+ self.assertEqual(last, user.last_edit)
+
+ def test_logevents(self):
+ """Test the User.logevents() method."""
+ mysite = self.get_site()
+ user = User(mysite, mysite.user())
+ le = list(user.logevents(total=10))
+ if not le:
+ self.skipTest('User {0} has no logevents on site {1}.'
+ .format(mysite.user(), mysite))
+ self.assertLessEqual(len(le), 10)
+ last = le[0]
+ self.assertTrue(all(event.user() == user.username for event in le))
+ self.assertEqual(last, user.last_event)
+
+
if __name__ == '__main__': # pragma: no cover
try:
unittest.main()

To view, visit change 372790. To unsubscribe, visit settings.

Gerrit-Project: pywikibot/core
Gerrit-Branch: master
Gerrit-MessageType: merged
Gerrit-Change-Id: I1aa05649b47eff990f63d38663ff647604e7e260
Gerrit-Change-Number: 372790
Gerrit-PatchSet: 9
Gerrit-Owner: Xqt <info@gno.de>
Gerrit-Reviewer: Dalba <dalba.wiki@gmail.com>
Gerrit-Reviewer: Framawiki <framawiki@tools.wmflabs.org>
Gerrit-Reviewer: John Vandenberg <jayvdb@gmail.com>
Gerrit-Reviewer: Magul <tomasz.magulski@gmail.com>
Gerrit-Reviewer: Xqt <info@gno.de>
Gerrit-Reviewer: jenkins-bot <>