Revision: 7716
Author: siebrand
Date: 2009-11-30 11:44:07 +0000 (Mon, 30 Nov 2009)
Log Message:
-----------
[ #2895284 ] Port of the User() class. Contributed by stanlekub. Superfluous whitespace
removed by siebrand. Untested.
Comments from patch submit:
[..] this creates a class User(), which is a subclass of Page, with additional methods.
The patch adds:
* the class User() in page.py
* 2 new exceptions in exceptions.py
* an import statement for User() in __init__.py
* a minimal usrlib.py file is also provided for backwards-compatibility, in the same
manner it was done for catlib.
This implementation should be fully compatible with the "old" one... with all of
its defaults included. There is for example a strange discrepancy between the format of
timestamps as returned by .contributions() and .loadedImages().
The methods .block() and .unblock() aren't implemented yet, and the
'autoblock' thingy should probably be tweeked a little more.
Comments on this are really welcomed [..]
Modified Paths:
--------------
branches/rewrite/pywikibot/__init__.py
branches/rewrite/pywikibot/exceptions.py
branches/rewrite/pywikibot/page.py
Added Paths:
-----------
branches/rewrite/pywikibot/userlib.py
Modified: branches/rewrite/pywikibot/__init__.py
===================================================================
--- branches/rewrite/pywikibot/__init__.py 2009-11-30 11:36:45 UTC (rev 7715)
+++ branches/rewrite/pywikibot/__init__.py 2009-11-30 11:44:07 UTC (rev 7716)
@@ -140,7 +140,7 @@
"""
logger = logging.getLogger("pywiki.wiki")
-
+
if code is None:
code = config.mylang
if fam is None:
@@ -172,7 +172,7 @@
getSite = Site # alias for backwards-compability
-from page import Page, ImagePage, Category, Link
+from page import Page, ImagePage, Category, Link, User
link_regex = re.compile(r'\[\[(?P<title>[^\]|[#<>{}]*)(\|.*?)?\]\]')
@@ -209,7 +209,7 @@
Output a string showing the differences between oldtext and newtext.
The differences are highlighted (only on compatible systems) to show which
changes were made.
-
+
"""
# This is probably not portable to non-terminal interfaces....
# For information on difflib, see
http://pydoc.org/2.3/difflib.html
Modified: branches/rewrite/pywikibot/exceptions.py
===================================================================
--- branches/rewrite/pywikibot/exceptions.py 2009-11-30 11:36:45 UTC (rev 7715)
+++ branches/rewrite/pywikibot/exceptions.py 2009-11-30 11:44:07 UTC (rev 7716)
@@ -29,7 +29,7 @@
return self.unicode
class PageRelatedError(Error):
- """Abstract Exception, used when the Exception concerns a particular
+ """Abstract Exception, used when the Exception concerns a particular
Page, and when a generic message can be written once for all"""
# Preformated UNICODE message where the page title will be inserted
# Override this in subclasses.
@@ -68,7 +68,7 @@
class CircularRedirect(Error):
"""Page is a circular redirect
-
+
Exception argument is the redirect target; this may be the same title
as this page or a different title (in which case the target page directly
or indirectly redirects back to this one)
@@ -119,3 +119,11 @@
class UploadWarning(Error):
"""Upload failed with a warning message (passed as the
argument)."""
+class AutoblockUser(Error):
+ """
+ The class AutoblockUserError is an exception that is raised whenever
+ an action is requested on a virtual autoblock user that's not available
+ for him (i.e. roughly everything except unblock).
+ """
+class UserActionRefuse(Error):
+ pass
Modified: branches/rewrite/pywikibot/page.py
===================================================================
--- branches/rewrite/pywikibot/page.py 2009-11-30 11:36:45 UTC (rev 7715)
+++ branches/rewrite/pywikibot/page.py 2009-11-30 11:44:07 UTC (rev 7716)
@@ -1753,6 +1753,243 @@
return sorted(list(set(self.categories())))
+class User(Page):
+ """A class that represents a Wiki user.
+ """
+
+ @deprecate_arg("insite", None)
+ def __init__(self, source, title=u''):
+ """All parameters are the same as for Page() constructor.
+ """
+ if len(title) > 1 and title[0] == u'#':
+ self.is_autoblock = True
+ title = title[1:]
+ else:
+ self.is_autoblock = False
+ Page.__init__(self, source, title, ns=2)
+ if self.namespace() != 2:
+ raise ValueError(u"'%s' is not in the user namespace!"
+ % title)
+ if self.is_autoblock:
+ # This user is probably being queried for purpose of lifting
+ # an autoblock.
+ pywikibot.output("This is an autoblock ID, "
+ "you can only use to unblock it.")
+
+ @property
+ def username(self):
+ """ Convenience method that returns the title of the page with
+ namespace prefix omitted, aka the username, as a Unicode string.
+ """
+ if self.is_autoblock:
+ return u'#' + self.title(withNamespace=False)
+ else:
+ return self.title(withNamespace=False)
+
+ def getprops(self, force=False):
+ """ Return a Dictionnary that contains user's properties. Use
cached
+ values if already called before, otherwise fetch data from the API.
+
+ @param force: if True, forces reloading the data from API
+ @type force: bool
+ """
+ if force:
+ del self._userprops
+ if not hasattr(self, '_userprops'):
+ usrequest = pywikibot.data.api.Request(
+ site=self.site(),
+ action='query',
+ list='users',
+
usprop='blockinfo|groups|editcount|registration|emailable',
+ ususers=self.username,
+ )
+ usdata = usrequest.submit()
+ assert 'query' in usdata, \
+ "API users response lacks 'query' key"
+ assert 'users' in usdata['query'], \
+ "API users response lacks 'users' key"
+ if u'missing' in usdata['query']['users'][0] or \
+ u'invalid' in
usdata['query']['users'][0]:
+ raise pywikibot.Error(u'No such user or invaild username (%s)'\
+ % self.username)
+ self._userprops = usdata['query']['users'][0]
+ return self._userprops
+
+ def registrationTime(self, force=False):
+ """ Return registration time for this user, as a Unicode string
in
+ ISO8601 format, or None if the date is unknown.
+
+ @param force: if True, forces reloading the data from API
+ @type force: bool
+ """
+ if 'registration' in self.getprops(force):
+ return self.getprops()['registration']
+
+ def editCount(self, force=False):
+ """ Return edit count for this user as int.
+
+ @param force: if True, forces reloading the data from API
+ @type force: bool
+ """
+ if 'editcount' in self.getprops(force):
+ return self.getprops()['editcount']
+ else:
+ return 0
+
+ def isBlocked(self, force=False):
+ """ Return True if this user is currently blocked, False
otherwise.
+
+ @param force: if True, forces reloading the data from API
+ @type force: bool
+ """
+ return 'blockedby' in self.getprops(force)
+
+ def isEmailable(self, force=False):
+ """ Return True if emails can be send to this user through
mediawiki,
+ False otherwise.
+
+ @param force: if True, forces reloading the data from API
+ @type force: bool
+ """
+ return 'emailable' in self.getprops(force)
+
+ def groups(self, force=False):
+ """ Return a list of groups to wich this user belongs. The return
value
+ is guaranteed to be a list object, possibly empty.
+
+ @param force: if True, forces reloading the data from API
+ @type force: bool
+ """
+ if 'groups' in self.getprops(force):
+ return self.getprops()['groups']
+ else:
+ return []
+
+ def getUserPage(self, subpage=u''):
+ """ Return a pywikibot.Page object corresponding to this
user's main
+ page, or a subpage of it if subpage is set.
+
+ @param subpage: subpage part to be appended to the main
+ page title (optional)
+ @type subpage: unicode
+ """
+ if self.is_autoblock:
+ #This user is probably being queried for purpose of lifting
+ #an autoblock, so has no user pages per se.
+ raise AutoblockUser("This is an autoblock ID, you can only use to
unblock it.")
+ if subpage:
+ subpage = u'/' + subpage
+ return Page(Link(self.title() + subpage, self.site()))
+
+ def getUserTalkPage(self, subpage=u''):
+ """ Return a pywikibot.Page object corresponding to this
user's main
+ talk page, or a subpage of it if subpage is set.
+
+ @param subpage: subpage part to be appended to the main
+ talk page title (optional)
+ @type subpage: unicode
+ """
+ if self.is_autoblock:
+ #This user is probably being queried for purpose of lifting
+ #an autoblock, so has no user talk pages per se.
+ raise AutoblockUser("This is an autoblock ID, you can only use to
unblock it.")
+ if subpage:
+ subpage = u'/' + subpage
+ return Page(Link(self.title(withNamespace=False) + subpage,
+ self.site(), defaultNamespace=3))
+
+ def sendMail(self, subject, text, ccme = False):
+ """ Send an email to this user via mediawiki's email
interface.
+ Return True on success, False otherwise.
+ This method can raise an UserActionRefuse exception in case this user
+ doesn't allow sending email to him or the currently logged in bot
+ doesn't have the right to send emails.
+
+ @param subject: the subject header of the mail
+ @type subject: unicode
+ @param text: mail body
+ @type text: unicode
+ @param ccme: if True, sends a copy of this email to the bot
+ @type ccme: bool
+ """
+ if not self.isEmailable():
+ raise UserActionRefuse('This user is not mailable')
+
+ if not self.site().has_right('sendemail'):
+ raise UserActionRefuse('You don\'t have permission to send
mail')
+
+ params = {
+ 'action': 'emailuser',
+ 'target': self.username,
+ 'token': self.site().token(self, 'email'),
+ 'subject': subject,
+ 'text': text,
+ }
+ if ccme:
+ params['ccme'] = 1
+ mailrequest = pywikibot.data.api.Request(**params)
+ maildata = mailrequest.submit()
+
+ if 'error' in maildata:
+ code = maildata['error']['code']
+ if code == u'usermaildisabled ':
+ pywikibot.output(u'User mail has been disabled')
+ elif 'emailuser' in maildata:
+ if maildata['emailuser']['result'] == u'Success':
+ pywikibot.output(u'Email sent.')
+ return True
+ return False
+
+ @deprecated("contributions")
+ @deprecate_arg("limit", "total") # To be consistent with rest of
framework
+ def editedPages(self, total=500):
+ """ Deprecated function that wraps 'contributions' for
backwards
+ compatibility. Yields pywikibot.Page objects that this user has
+ edited, with an upper bound of 'total'. Pages returned are not
+ guaranteed to be unique.
+
+ @param total: limit result to this number of pages.
+ @type total: int.
+ """
+ for item in self.contributions(total=total):
+ yield item[0]
+
+ @deprecate_arg("limit", "total") # To be consistent with rest of
framework
+ @deprecate_arg("namespace", "namespaces")
+ def contributions(self, total=500, namespaces=[]):
+ """ Yield tuples describing this user edits.
+ Each tuple is composed of a pywikibot.Page object,
+ the revision id (int), the edit timestamp (as int in mediawiki's
+ internal format), and the comment (unicode).
+ Pages returned are not guaranteed to be unique.
+
+ @param total: limit result to this number of pages
+ @type total: int
+ @param namespaces: only iterate links in these namespaces
+ @type namespaces: list
+ """
+ for contrib in self.site().usercontribs(user=self.username,
+ namespaces=namespaces, total=total):
+ ts = pywikibot.Timestamp.fromISOformat(contrib['timestamp'])
+ ts = int(ts.strftime("%Y%m%d%H%M%S"))
+ yield Page(Link(contrib['title'], self.site(),
+ defaultNamespace=contrib['ns'])), \
+ contrib['revid'], ts, contrib['comment']
+
+ @deprecate_arg("number", "total")
+ def uploadedImages(self, total=10):
+ """ Yield tuples describing files uploaded by this user.
+ Each tuple is composed of a pywikibot.Page, the timestamp (str in
+ ISO8601 format), comment (unicode) and a bool (always False...).
+ Pages returned are not guaranteed to be unique.
+
+ @param total: limit result to this number of pages
+ @type total: int
+ """
+ for item in self.site().logevents(logtype='upload', user=self.username,
+ total=total):
+ yield item.title(), str(item.timestamp()), item.comment(), False
+
class Revision(object):
"""A structure holding information about a single revision of a
Page."""
def __init__(self, revid, timestamp, user, anon=False, comment=u"",
@@ -2261,4 +2498,3 @@
pass
# Couldn't convert, raise the original exception
raise firstException
-
Added: branches/rewrite/pywikibot/userlib.py
===================================================================
--- branches/rewrite/pywikibot/userlib.py (rev 0)
+++ branches/rewrite/pywikibot/userlib.py 2009-11-30 11:44:07 UTC (rev 7716)
@@ -0,0 +1,17 @@
+# -*- coding: utf-8 -*-
+"""
+WARNING: THIS MODULE EXISTS SOLELY TO PROVIDE BACKWARDS-COMPATIBILITY.
+
+Do not use in new scripts; use the source to find the appropriate
+function/method instead.
+
+"""
+#
+# (C) Pywikipedia bot team, 2008
+#
+# Distributed under the terms of the MIT license.
+#
+__version__ = '$Id$'
+
+
+from pywikibot import User
Property changes on: branches/rewrite/pywikibot/userlib.py
___________________________________________________________________
Added: svn:keywords
+ Id
Added: svn:eol-style
+ native