0001"""
0002General-(web-)purpose functions for use in your applications.
0003"""
0004import cgi
0005import urllib
0006import re
0007import datetime
0008
0009__all__ = ['html_quote', 'url_quote', 'html_quote_whitespace']
0010
0011def html_quote(v):
0012    """
0013    HTML quote the value.  None becomes an empty string.  If a value
0014    has an ``__html__`` method, that will be called to get the HTML.
0015    """
0016    if v is None:
0017        return ''
0018    if hasattr(v, '__html__'):
0019        return v.__html__()
0020    if isinstance(v, basestring):
0021        return cgi.escape(v, 1)
0022    return cgi.escape(str(v), 1)
0023
0024def url_quote(v):
0025    """
0026    URL quote the value.  None becomes the empty string.  Dictionaries
0027    are mapped to 
0028    """
0029    if isinstance(v, dict):
0030        return urllib.urlencode(v)
0031    elif v is None:
0032        return ''
0033    else:
0034        return urllib.urlquote(str(v))
0035
0036_whitespace_re = re.compile(r'[ ][ ]+')
0037
0038def html_quote_whitespace(v, newlines=True, strip=True):
0039    """
0040    HTML quote a value, and then make sure its whitespace is
0041    preserved.  If ``newlines`` is true, then also turn newlines into
0042    ``<br>`` -- otherwise just horizontal whitespace will be preserved
0043    (using ``&nbsp;``).
0044    """
0045    v = html_quote(v)
0046    if strip:
0047        v = v.strip()
0048    v = v.replace('\t', ' '*4)
0049    if newlines:
0050        lines = v.splitlines()
0051        for i in range(len(lines)):
0052            if lines[i].startswith(' '):
0053                lines[i] = '&nbsp;'+lines[i][1:]
0054        v = '<br />\n'.join(lines)
0055    v = _whitespace_re.sub(_sub_white, v)
0056    return v
0057
0058def _sub_white(m):
0059    if len(m.group(0)) % 2:
0060        return m.group(0).replace('  ', '&nbsp; ')
0061    else:
0062        return '&nbsp;'+'&nbsp; '*(len(m.group(0))/2)
0063
0064def format_date_relative(date):
0065    """
0066    Formats a date relative to the current time.  The result
0067    will be dates like 'Yesterday', 'Wednesday 10:00am',
0068    '12 May 2003', etc.
0069
0070    Specifically:
0071
0072      * If in the last 24 hours, just give the time.
0073      * If yesterday, give 'yesterday TIME'
0074      * If in the last 7 days, give 'DAY_OF_WEEK TIME'
0075      * If in the same calendar year, give 'DAY_OF_MONTH MONTH'
0076      * Otherwise gives 'DAY_OF_MONTH MONTH YEAR'
0077
0078    It uses english names when appropriate (e.g., Apr or Thu).
0079    """
0080    if date is None:
0081        return ''
0082    if isinstance(date, datetime.datetime):
0083        now = datetime.datetime.now()
0084    elif isinstance(date, datetime.date):
0085        now = datetime.date.today()
0086    else:
0087        raise ValueError(
0088            "Unknown type of date: %r" % date)
0089    year = date.year
0090    month = date.month
0091    day = date.day
0092    if now.year == year:
0093        if now.month == month:
0094            if (now - date) < datetime.timedelta(days=7):
0095                if now.day == day:
0096                    return format_time(date)
0097                elif now.day - 1 == day:
0098                    return 'Yest %s' % format_time(date)
0099                else:
0100                    return date.strftime('%a ') + format_time(date)
0101            elif (now - date) < datetime.timedelta(days=7):
0102                return date.strftime('%a %d %b')
0103            else:
0104                return date.strftime('%d %b')
0105        else:
0106            return date.strftime('%d %b')
0107    else:
0108        return date.strftime('%d %b \'%y')
0109
0110def format_time(t):
0111    """
0112    Formats the time, like 4:20pm; unlike strftime, it lower-cases
0113    the am/pm, and doesn't create hours with leading zeros.
0114    """
0115    text = t.strftime('%I:%M%p')
0116    text = text[:-2] + text[-2:].lower()
0117    if text.startswith('0'):
0118        text = text[1:]
0119    return text