|
@@ -1,5 +1,7 @@ |
|
|
# -*- coding: utf-8 -*- |
|
|
# -*- coding: utf-8 -*- |
|
|
|
|
|
|
|
|
|
|
|
from time import strptime |
|
|
|
|
|
|
|
|
from wiki.tools.constants import * |
|
|
from wiki.tools.constants import * |
|
|
from wiki.tools.exceptions import UserNotFoundError |
|
|
from wiki.tools.exceptions import UserNotFoundError |
|
|
from wiki.tools.page import Page |
|
|
from wiki.tools.page import Page |
|
@@ -7,34 +9,53 @@ from wiki.tools.page import Page |
|
|
class User(object): |
|
|
class User(object): |
|
|
""" |
|
|
""" |
|
|
EarwigBot's Wiki Toolset: User Class |
|
|
EarwigBot's Wiki Toolset: User Class |
|
|
|
|
|
|
|
|
|
|
|
Represents a User on a given Site. Has methods for getting a bunch of |
|
|
|
|
|
information about the user, such as editcount and user rights, methods for |
|
|
|
|
|
returning the user's userpage and talkpage, etc. |
|
|
|
|
|
|
|
|
|
|
|
Public methods: |
|
|
|
|
|
name -- returns the user's username |
|
|
|
|
|
exists -- returns True if the user exists, False if they do not |
|
|
|
|
|
userid -- returns an integer ID representing the user |
|
|
|
|
|
blockinfo -- returns information about a current block on the user |
|
|
|
|
|
groups -- returns a list of the user's groups |
|
|
|
|
|
rights -- returns a list of the user's rights |
|
|
|
|
|
editcount -- returns the number of edits made by the user |
|
|
|
|
|
registration -- returns the time the user registered as a time.struct_time |
|
|
|
|
|
emailable -- returns True if you can email the user, False if you cannot |
|
|
|
|
|
gender -- returns the user's gender ("male", "female", or "unknown") |
|
|
|
|
|
get_userpage -- returns a Page object representing the user's userpage |
|
|
|
|
|
get_talkpage -- returns a Page object representing the user's talkpage |
|
|
""" |
|
|
""" |
|
|
|
|
|
|
|
|
def __init__(self, site, name): |
|
|
def __init__(self, site, name): |
|
|
""" |
|
|
|
|
|
Docstring needed |
|
|
|
|
|
""" |
|
|
|
|
|
# Site instance, for doing API queries, etc |
|
|
|
|
|
self.site = site |
|
|
|
|
|
|
|
|
"""Constructor for new User instances. |
|
|
|
|
|
|
|
|
# Username |
|
|
|
|
|
self._name = name |
|
|
|
|
|
|
|
|
Takes two arguments, a Site object (necessary for doing API queries), |
|
|
|
|
|
and the name of the user, preferably without "User:" in front, although |
|
|
|
|
|
this prefix will be automatically removed by the API if given. |
|
|
|
|
|
|
|
|
# Attributes filled in by an API query |
|
|
|
|
|
self._exists = None |
|
|
|
|
|
self._userid = None |
|
|
|
|
|
self._blockinfo = None |
|
|
|
|
|
self._groups = None |
|
|
|
|
|
self._rights = None |
|
|
|
|
|
self._editcount = None |
|
|
|
|
|
self._registration = None |
|
|
|
|
|
self._emailable = None |
|
|
|
|
|
self._gender = None |
|
|
|
|
|
|
|
|
You can also use site.get_user() instead, which returns a User object, |
|
|
|
|
|
and is preferred. |
|
|
|
|
|
|
|
|
def _get_attribute(self, attr, force): |
|
|
|
|
|
|
|
|
We won't do any API queries yet for basic information about the user - |
|
|
|
|
|
save that for when the information is requested. |
|
|
""" |
|
|
""" |
|
|
Docstring needed |
|
|
|
|
|
|
|
|
self._site = site |
|
|
|
|
|
self._name = name |
|
|
|
|
|
|
|
|
|
|
|
def _get_attribute(self, attr, force): |
|
|
|
|
|
"""Internally used to get an attribute by name. |
|
|
|
|
|
|
|
|
|
|
|
We'll call _load_attributes() to get this (and all other attributes) |
|
|
|
|
|
from the API if it is not already defined. If `force` is True, we'll |
|
|
|
|
|
re-load them even if they've already been loaded. |
|
|
|
|
|
|
|
|
|
|
|
Raises UserNotFoundError if a nonexistant user prevents us from |
|
|
|
|
|
returning a certain attribute. |
|
|
""" |
|
|
""" |
|
|
if self._exists is None or force: |
|
|
|
|
|
|
|
|
if not hasattr(self, attr) or force: |
|
|
self._load_attributes() |
|
|
self._load_attributes() |
|
|
if self._exists is False: |
|
|
if self._exists is False: |
|
|
e = "User '{0}' does not exist.".format(self._name) |
|
|
e = "User '{0}' does not exist.".format(self._name) |
|
@@ -42,30 +63,42 @@ class User(object): |
|
|
return getattr(self, attr) |
|
|
return getattr(self, attr) |
|
|
|
|
|
|
|
|
def _load_attributes(self): |
|
|
def _load_attributes(self): |
|
|
""" |
|
|
|
|
|
Docstring needed |
|
|
|
|
|
|
|
|
"""Internally used to load all attributes from the API. |
|
|
|
|
|
|
|
|
|
|
|
Normally, this is called by _get_attribute() when a requested attribute |
|
|
|
|
|
is not defined. This defines it. |
|
|
""" |
|
|
""" |
|
|
params = {"action": "query", "list": "users", "ususers": self._name, |
|
|
params = {"action": "query", "list": "users", "ususers": self._name, |
|
|
"usprop": "blockinfo|groups|rights|editcount|registration|emailable|gender"} |
|
|
"usprop": "blockinfo|groups|rights|editcount|registration|emailable|gender"} |
|
|
result = self.site.api_query(params) |
|
|
|
|
|
|
|
|
result = self._site.api_query(params) |
|
|
|
|
|
res = result["query"]["users"][0] |
|
|
|
|
|
|
|
|
# normalize our username in case it was entered oddly |
|
|
# normalize our username in case it was entered oddly |
|
|
self._name = result["query"]["users"][0]["name"] |
|
|
|
|
|
|
|
|
self._name = res["name"] |
|
|
|
|
|
|
|
|
try: |
|
|
try: |
|
|
self._userid = result["query"]["users"][0]["userid"] |
|
|
|
|
|
|
|
|
self._userid = res["userid"] |
|
|
except KeyError: # userid is missing, so user does not exist |
|
|
except KeyError: # userid is missing, so user does not exist |
|
|
self._exists = False |
|
|
self._exists = False |
|
|
return |
|
|
return |
|
|
|
|
|
|
|
|
self._exists = True |
|
|
self._exists = True |
|
|
res = result['query']['users'][0] |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
try: |
|
|
|
|
|
self._blockinfo = { |
|
|
|
|
|
"by": res["blockedby"], |
|
|
|
|
|
"reason": res["blockreason"], |
|
|
|
|
|
"expiry": res["blockexpiry"] |
|
|
|
|
|
} |
|
|
|
|
|
except KeyError: |
|
|
|
|
|
self._blockinfo = False |
|
|
|
|
|
|
|
|
self._groups = res["groups"] |
|
|
self._groups = res["groups"] |
|
|
self._rights = res["rights"] |
|
|
|
|
|
|
|
|
self._rights = res["rights"].values() |
|
|
self._editcount = res["editcount"] |
|
|
self._editcount = res["editcount"] |
|
|
self._registration = res["registration"] |
|
|
|
|
|
self._gender = res["gender"] |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
reg = res["registration"] |
|
|
|
|
|
self._registration = strptime(reg, "%Y-%m-%dT%H:%M:%SZ") |
|
|
|
|
|
|
|
|
try: |
|
|
try: |
|
|
res["emailable"] |
|
|
res["emailable"] |
|
@@ -74,88 +107,120 @@ class User(object): |
|
|
else: |
|
|
else: |
|
|
self._emailable = True |
|
|
self._emailable = True |
|
|
|
|
|
|
|
|
try: |
|
|
|
|
|
self._blockinfo = {"by": res["blockedby"], |
|
|
|
|
|
"reason": res["blockreason"], "expiry": res["blockexpiry"]} |
|
|
|
|
|
except KeyError: |
|
|
|
|
|
self._blockinfo = False |
|
|
|
|
|
|
|
|
self._gender = res["gender"] |
|
|
|
|
|
|
|
|
def name(self, force=False): |
|
|
def name(self, force=False): |
|
|
""" |
|
|
|
|
|
Docstring needed |
|
|
|
|
|
|
|
|
"""Returns the user's name. |
|
|
|
|
|
|
|
|
|
|
|
If `force` is True, we will load the name from the API and return that. |
|
|
|
|
|
This could potentially return a "normalized" version of the name - for |
|
|
|
|
|
example, without a "User:" prefix or without underscores. Unlike other |
|
|
|
|
|
attribute getters, this will never make an API query without `force`. |
|
|
|
|
|
|
|
|
|
|
|
Note that if another attribute getter, like exists(), has already been |
|
|
|
|
|
called, then the username has already been normalized. |
|
|
""" |
|
|
""" |
|
|
if force: |
|
|
if force: |
|
|
self._load_attributes() |
|
|
self._load_attributes() |
|
|
return self._name |
|
|
return self._name |
|
|
|
|
|
|
|
|
def exists(self, force=False): |
|
|
def exists(self, force=False): |
|
|
""" |
|
|
|
|
|
Docstring needed |
|
|
|
|
|
|
|
|
"""Returns True if the user exists, or False if they do not. |
|
|
|
|
|
|
|
|
|
|
|
Makes an API query if `force` is True or if we haven't made one |
|
|
|
|
|
already. |
|
|
""" |
|
|
""" |
|
|
if self._exists is None or force: |
|
|
if self._exists is None or force: |
|
|
self._load_attributes() |
|
|
self._load_attributes() |
|
|
return self._exists |
|
|
return self._exists |
|
|
|
|
|
|
|
|
def userid(self, force=False): |
|
|
def userid(self, force=False): |
|
|
""" |
|
|
|
|
|
Docstring needed |
|
|
|
|
|
|
|
|
"""Returns an integer ID used by MediaWiki to represent the user. |
|
|
|
|
|
|
|
|
|
|
|
Raises UserNotFoundError if the user does not exist. Makes an API query |
|
|
|
|
|
if `force` is True or if we haven't made one already. |
|
|
""" |
|
|
""" |
|
|
return self._get_attribute("_userid", force) |
|
|
return self._get_attribute("_userid", force) |
|
|
|
|
|
|
|
|
def blockinfo(self, force=False): |
|
|
def blockinfo(self, force=False): |
|
|
""" |
|
|
|
|
|
Docstring needed |
|
|
|
|
|
|
|
|
"""Returns information about a current block on the user. |
|
|
|
|
|
|
|
|
|
|
|
If the user is not blocked, returns False. If they are, returns a dict |
|
|
|
|
|
with three keys: "by" is the blocker's username, "reason" is the reason |
|
|
|
|
|
why they were blocked, and "expiry" is when the block expires. |
|
|
|
|
|
|
|
|
|
|
|
Raises UserNotFoundError if the user does not exist. Makes an API query |
|
|
|
|
|
if `force` is True or if we haven't made one already. |
|
|
""" |
|
|
""" |
|
|
return self._get_attribute("_blockinfo", force) |
|
|
return self._get_attribute("_blockinfo", force) |
|
|
|
|
|
|
|
|
def groups(self, force=False): |
|
|
def groups(self, force=False): |
|
|
""" |
|
|
|
|
|
Docstring needed |
|
|
|
|
|
|
|
|
"""Returns a list of groups this user is in, including "*". |
|
|
|
|
|
|
|
|
|
|
|
Raises UserNotFoundError if the user does not exist. Makes an API query |
|
|
|
|
|
if `force` is True or if we haven't made one already. |
|
|
""" |
|
|
""" |
|
|
return self._get_attribute("_groups", force) |
|
|
return self._get_attribute("_groups", force) |
|
|
|
|
|
|
|
|
def rights(self, force=False): |
|
|
def rights(self, force=False): |
|
|
""" |
|
|
|
|
|
Docstring needed |
|
|
|
|
|
|
|
|
"""Returns a list of this user's rights. |
|
|
|
|
|
|
|
|
|
|
|
Raises UserNotFoundError if the user does not exist. Makes an API query |
|
|
|
|
|
if `force` is True or if we haven't made one already. |
|
|
""" |
|
|
""" |
|
|
return self._get_attribute("_rights", force) |
|
|
return self._get_attribute("_rights", force) |
|
|
|
|
|
|
|
|
def editcount(self, force=False): |
|
|
def editcount(self, force=False): |
|
|
""" |
|
|
|
|
|
Docstring needed |
|
|
|
|
|
|
|
|
"""Returns the number of edits made by the user. |
|
|
|
|
|
|
|
|
|
|
|
Raises UserNotFoundError if the user does not exist. Makes an API query |
|
|
|
|
|
if `force` is True or if we haven't made one already. |
|
|
""" |
|
|
""" |
|
|
return self._get_attribute("_editcount", force) |
|
|
return self._get_attribute("_editcount", force) |
|
|
|
|
|
|
|
|
def registration(self, force=False): |
|
|
def registration(self, force=False): |
|
|
""" |
|
|
|
|
|
Docstring needed |
|
|
|
|
|
|
|
|
"""Returns the time the user registered as a time.struct_time object. |
|
|
|
|
|
|
|
|
|
|
|
Raises UserNotFoundError if the user does not exist. Makes an API query |
|
|
|
|
|
if `force` is True or if we haven't made one already. |
|
|
""" |
|
|
""" |
|
|
return self._get_attribute("_registration", force) |
|
|
return self._get_attribute("_registration", force) |
|
|
|
|
|
|
|
|
def emailable(self, force=False): |
|
|
def emailable(self, force=False): |
|
|
""" |
|
|
|
|
|
Docstring needed |
|
|
|
|
|
|
|
|
"""Returns True if the user can be emailed, or False if they cannot. |
|
|
|
|
|
|
|
|
|
|
|
Raises UserNotFoundError if the user does not exist. Makes an API query |
|
|
|
|
|
if `force` is True or if we haven't made one already. |
|
|
""" |
|
|
""" |
|
|
return self._get_attribute("_emailable", force) |
|
|
return self._get_attribute("_emailable", force) |
|
|
|
|
|
|
|
|
def gender(self, force=False): |
|
|
def gender(self, force=False): |
|
|
""" |
|
|
|
|
|
Docstring needed |
|
|
|
|
|
|
|
|
"""Returns the user's gender. |
|
|
|
|
|
|
|
|
|
|
|
Can return either "male", "female", or "unknown", if they did not |
|
|
|
|
|
specify it. |
|
|
|
|
|
|
|
|
|
|
|
Raises UserNotFoundError if the user does not exist. Makes an API query |
|
|
|
|
|
if `force` is True or if we haven't made one already. |
|
|
""" |
|
|
""" |
|
|
return self._get_attribute("_gender", force) |
|
|
return self._get_attribute("_gender", force) |
|
|
|
|
|
|
|
|
def get_userpage(self): |
|
|
def get_userpage(self): |
|
|
|
|
|
"""Returns a Page object representing the user's userpage. |
|
|
|
|
|
|
|
|
|
|
|
No checks are made to see if it exists or not. Proper site namespace |
|
|
|
|
|
conventions are followed. |
|
|
""" |
|
|
""" |
|
|
Docstring needed |
|
|
|
|
|
""" |
|
|
|
|
|
prefix = self.site.namespace_id_to_name(NS_USER) |
|
|
|
|
|
|
|
|
prefix = self._site.namespace_id_to_name(NS_USER) |
|
|
pagename = ''.join((prefix, ":", self._name)) |
|
|
pagename = ''.join((prefix, ":", self._name)) |
|
|
return Page(self.site, pagename) |
|
|
|
|
|
|
|
|
return Page(self._site, pagename) |
|
|
|
|
|
|
|
|
def get_talkpage(self): |
|
|
def get_talkpage(self): |
|
|
|
|
|
"""Returns a Page object representing the user's talkpage. |
|
|
|
|
|
|
|
|
|
|
|
No checks are made to see if it exists or not. Proper site namespace |
|
|
|
|
|
conventions are followed. |
|
|
""" |
|
|
""" |
|
|
Docstring needed |
|
|
|
|
|
""" |
|
|
|
|
|
prefix = self.site.namespace_id_to_name(NS_USER_TALK) |
|
|
|
|
|
|
|
|
prefix = self._site.namespace_id_to_name(NS_USER_TALK) |
|
|
pagename = ''.join((prefix, ":", self._name)) |
|
|
pagename = ''.join((prefix, ":", self._name)) |
|
|
return Page(self.site, pagename) |
|
|
|
|
|
|
|
|
return Page(self._site, pagename) |