#! /usr/bin/env python
"""
Support for the "Person" class, and related stuff

Handles
    - name-structuring conventions
    - list of people on a web (wiki) page

"""
__version__ = "$Revision: 1.6 $"


import sys
import re

import xmlextras
import html 

namePattern  = re.compile(r'''^([^ ]*) ((([^ (]+) )*)(\(([^)]*)\) )?(.*)$''')
class Person:
    """
    Represents a human (or pseudo-human) participant in an activity.
    
    >>> p = Person("Sandro Hawke")
    >>> print `p.first`
    'Sandro'
    >>> print `p.middle`
    ''
    >>> print `p.nicks`
    []
    >>> print `p.last`
    'Hawke'
    >>> p.toWiki()
    '[[Sandro Hawke]]'
    >>> print p.toHTML()
    <span>Sandro Hawke</span>
    >>> p.url = 'http://www.w3.org/2007/OWL/wiki/Sandro_Hawke'
    >>> print p.toHTML()
    <a href="http://www.w3.org/2007/OWL/wiki/Sandro_Hawke">Sandro Hawke</a>
    >>> p.matches("sandro")
    True
    >>> p.matches("Hawke")
    True
    >>> p.matches("sHawke")
    True
    >>> p.matches("sandroh")
    True
    >>> p.matches("san")
    True
    
    >>> p = Person("George H. W. Bush")
    >>> print `p.first`
    'George'
    >>> print `p.middle`
    'H. W. '
    >>> print `p.nicks`
    []
    >>> print `p.last`
    'Bush'

    >>> p = Person("Christian (csma, csm) de Sainte Marie")
    >>> print `p.first`
    'Christian'
    >>> print `p.middle`
    ''
    >>> print `p.nicks`
    ['csma', 'csm']
    >>> print `p.last`
    'de Sainte Marie'


    >>> p = Person("Bernardo () Cuenca Grau")
    >>> print `p.first`
    'Bernardo'
    >>> print `p.middle`
    ''
    >>> print `p.nicks`
    []
    >>> print `p.last`
    'Cuenca Grau'
    >>> p.matches('bcuencagrau')
    True

    """
    
    def __init__(self, name, isBot=False, url=None):
        self.name = name
        self.isBot = isBot
        self.url = url
        m = namePattern.match(name)
        assert m
        self.first = m.group(1)
        self.middle = m.group(2)
        self.last = m.group(7)
        nicks = m.group(6)
        if nicks:
            self.nicks = nicks.split(", ")
        else:
            self.nicks = []

        self.lfirst = self.first.lower()
        self.lmiddle = self.middle.lower()
        self.llast = self.last.lower()
        self.lnicks = [x.lower() for x in self.nicks]

        self.llastjoined = "".join(self.llast.split())

    def __repr__(self):
        return "Person(%s, %s, %s)" % (`self.name`, `self.isBot`,
                                       `self.url`)
    def matches(self, text):
        text = text.lower()
        for nick in self.lnicks:
            if text == nick:
                return True
        if ( text == (self.lfirst + " " + self.llast) or
             text == (self.lfirst + "_" + self.llast) or
             text == (self.lfirst + self.llast) or
             text == (self.lfirst + self.llast[0]) or
             text == (self.lfirst[0] + self.llast) or
             text == self.lfirst or
             text == self.llast or
             self.lfirst.startswith(text) or 
             self.llast.startswith(text) or
             # alanru   Alan Ruttenberg
             (text[0:len(self.lfirst)] == self.lfirst and
              self.llast.startswith(text[len(self.lfirst):])) or
             # clu  Carsten Lutz
             (text[0:1] == (self.lfirst[0]) and
              self.llast.startswith(text[1:])) or
             # m_schnei   Michael Schneider
             (text[0:2] == (self.lfirst[0] + "_") and
              self.llast.startswith(text[2:])) or
             # bcuencagrau   Bernardo Cuenca Grau
             text == (self.lfirst[0] + self.llastjoined)
             ):
            return True
        return False
     
    def toWiki(self):
        return '[['+self.first+" "+self.last+"]]"

    def toHTML(self):
        if self.url:
            return html.a(self.first+" "+self.last, href=self.url)
        else:
            return html.span(self.first+" "+self.last)

class PageOfPeople:
    """

    >>> p = PageOfPeople('http://www.w3.org/2007/OWL/wiki/Participants2')
    >>> len(p.people)
    53
    >>> print [x for x in p.people if x.matches("sandro")]
    [Person('Sandro Hawke', False, u'/2007/OWL/wiki/Sandro_Hawke')]

    ....
    
    At some point, I think we'll need to add an"effective-date" parameter, and
    the page will need to include "(until yyyy-mm-dd)" and "(after
    yyyy-mm-dd)" to handle people entering/leaving the group.
    Without that, names will become ambiguous after the fact, and old
    minutes will no longer be parseable. 

    """
    
    def __init__(self, location):
        self.wikiPageURL = location
        self.people = []
        self.load()

    def load(self):
        self.people = []
        div = xmlextras.loadXMLFragment(self.wikiPageURL+"#people")
        for e in div.getElementsByTagName('li'):
            if self.tryPersonWithURL(e):
                pass
            elif self.tryPerson(e):
                pass
            else:
                raise RuntimeError, "badly formatted name, I think: %s"%`e`

    def tryPersonWithURL(self,xml):
        for child in xml.childNodes:
            if hasattr(child, "tagName") and child.tagName == "a":
                href = child.getAttribute("href")
                text = xmlextras.nodeContents(child).strip()
                text = text.encode('utf-8')
                p = Person(text, url=href)
                self.people.append(p)
                return True
        return False

    def tryPerson(xml):
        text = xmlextras.nodeContents(xml)
        p = Person(text)
        return True
    
if __name__ == "__main__":
    import doctest, sys
    doctest.testmod(sys.modules[__name__])

