Source code for omnipresence.util

"""General utility functions used within Omnipresence."""
import datetime
import re

from twisted.words.protocols import irc


DURATION_RE = re.compile(r'^(?:(\d+)w)?(?:(\d+)d)?(?:(\d+)h)?(?:(\d+)m)?(?:(\d+)s)?$',
                         re.IGNORECASE | re.VERBOSE)

DURATION_GROUPS = ['weeks', 'days', 'hours', 'minutes', 'seconds']

[docs]def ago(then, now=None): """Given a datetime object, return a string giving an approximate relative time, such as "5 days ago".""" if not now: now = datetime.datetime.now() delta = now - then if delta.days == 0: if delta.seconds < 10: return "just now" elif delta.seconds < 60: return "%d seconds ago" % (delta.seconds) elif delta.seconds < 120: return "a minute ago" elif delta.seconds < 3600: return "%d minutes ago" % (delta.seconds / 60) elif delta.seconds < 7200: return "an hour ago" else: return "%d hours ago" % (delta.seconds / 3600) elif delta.days == 1: return "yesterday" elif delta.days < 7: return "%d days ago" % (delta.days) elif delta.days < 14: return "a week ago" else: return "%d weeks ago" % (delta.days / 7)
[docs]def andify(seq, two_comma=False): """Given a list, join its elements and return a string of the form "*x* and *y*" for a two-element list, or "*x*, *y*, and *z*" for three or more elements. If *two_comma* is True, insert a comma before "and" even if the list is only two elements long ("*x*, and *y*").""" if len(seq) > 2: return ', '.join(seq[:-2] + [', and '.join(seq[-2:])]) if two_comma: return ', and '.join(seq) return ' and '.join(seq)
[docs]def duration_to_timedelta(duration): """Convert a duration of the form "?w?d?h?m?s" into a :py:class:`datetime.timedelta` object, where individual components are optional.""" match = DURATION_RE.match(duration) if match: kwargs = dict(((DURATION_GROUPS[i], int(value, 10)) for (i, value) in enumerate(match.groups('0')))) return datetime.timedelta(**kwargs) return datetime.timedelta(0)
[docs]def readable_duration(duration): """Convert a duration of the form "?w?d?h?m?s" to a readable string representation of the form "2 weeks, 5 days, and 20 hours".""" match = DURATION_RE.match(duration) if match: components = [] for (i, value) in enumerate(match.groups()): if value: unit = DURATION_GROUPS[i] value = int(value, 10) if value == 1: # Thankfully, all of these words are simple plurals. components.append(unit[:-1]) else: components.append('%d %s' % (value, unit)) return andify(components) return 'instant' # <http://stackoverflow.com/questions/1809531/-/1820949>
[docs]def truncate_unicode(s, char_limit, byte_limit, encoding='utf-8'): """Truncate a Unicode string so that it fits both within the specified character limit and, when encoded in the given encoding, the specified byte limit. Return the truncated string as a byte string.""" encoded = s[:char_limit].encode(encoding)[:byte_limit] return encoded.decode(encoding, 'ignore').encode(encoding)