# -*- coding: utf-8 -*- from time import gmtime, strptime from wiki.constants import * from wiki.exceptions import UserNotFoundError from wiki.page import Page class User(object): """ 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): """Constructor for new User instances. 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. You can also use site.get_user() instead, which returns a User object, and is preferred. We won't do any API queries yet for basic information about the user - save that for when the information is requested. """ self._site = site self._name = name def __repr__(self): """Returns the canonical string representation of the User.""" return "User(name={0!r}, site={1!r})".format(self._name, self._site) def __str__(self): """Returns a nice string representation of the User.""" return ''.format(self.name(), str(self._site)) 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 not hasattr(self, attr) or force: self._load_attributes() if self._exists is False: e = "User '{0}' does not exist.".format(self._name) raise UserNotFoundError(e) return getattr(self, attr) def _load_attributes(self): """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, "usprop": "blockinfo|groups|rights|editcount|registration|emailable|gender"} result = self._site._api_query(params) res = result["query"]["users"][0] # normalize our username in case it was entered oddly self._name = res["name"] try: self._userid = res["userid"] except KeyError: # userid is missing, so user does not exist self._exists = False return self._exists = True try: self._blockinfo = { "by": res["blockedby"], "reason": res["blockreason"], "expiry": res["blockexpiry"] } except KeyError: self._blockinfo = False self._groups = res["groups"] try: self._rights = res["rights"].values() except AttributeError: self._rights = res["rights"] self._editcount = res["editcount"] reg = res["registration"] try: self._registration = strptime(reg, "%Y-%m-%dT%H:%M:%SZ") except TypeError: # Sometimes the API doesn't give a date; the user's probably really # old. There's nothing else we can do! self._registration = gmtime(0) try: res["emailable"] except KeyError: self._emailable = False else: self._emailable = True self._gender = res["gender"] def name(self, force=False): """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: self._load_attributes() return self._name def exists(self, force=False): """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 not hasattr(self, "_exists") or force: self._load_attributes() return self._exists def userid(self, force=False): """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) def blockinfo(self, force=False): """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) def groups(self, force=False): """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) def rights(self, force=False): """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) def editcount(self, force=False): """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) def registration(self, force=False): """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) def emailable(self, force=False): """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) def gender(self, force=False): """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) 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. """ prefix = self._site.namespace_id_to_name(NS_USER) pagename = ':'.join((prefix, self._name)) return Page(self._site, pagename) 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. """ prefix = self._site.namespace_id_to_name(NS_USER_TALK) pagename = ':'.join((prefix, self._name)) return Page(self._site, pagename)