diff --git a/nxy/utils.py b/nxy/utils.py deleted file mode 100644 index d4ce693..0000000 --- a/nxy/utils.py +++ /dev/null @@ -1,87 +0,0 @@ -# -*- coding: utf-8 -*- -import re - -from datetime import datetime, timedelta -from pprint import pprint - -# @formatter:off -COLORS = dict( - white=0, # white - black=1, # black - blue=2, # blue (navy) - green=3, # green - red=4, # red - maroon=5, # brown (maroon) - purple=6, # purple - orange=7, # orange (olive) - yellow=8, # yellow - ltgreen=9, # light green (lime) - teal=10, # teal (a green/blue cyan) - ltcyan=11, # light cyan (cyan / aqua) - ltblue=12, # light blue (royal) - pink=13, # pink (light purple / fuchsia) - grey=14, # grey - ltgrey=15, # light grey (silver) -) - -FORMATTING = dict( - bold='\x02', # bold - color='\x03', # colored text - italic='\x1D', # italic text - underline='\x1F', # underlined text - swap='\x16', # swap bg and fg colors ("reverse video") - reset='\x0F', # reset all formatting - **COLORS -) -# @formatter:on - -# Debug helper -pp = pprint - - -def fmt(__text: str, **kwargs) -> str: - """Formats a str with `kwargs` and `FORMATTING`.""" - return __text.format(**kwargs, **FORMATTING) - - -def date_from_iso(date: str) -> datetime: - return datetime.strptime(date, '%Y-%m-%dT%H:%M:%S.%fZ') - - -def time_delta(text: str) -> timedelta: - match = re.match(r'(\d+)(s|m|h|mon|w|y)', text) - if match: - num, unit = match.groups() - num = int(num) - if unit == 's': - unit = 'seconds' - elif unit == 'm': - unit = 'minutes' - elif unit == 'h': - unit = 'hours' - elif unit == 'd': - unit = 'days' - elif unit == 'w': - unit = 'weeks' - elif unit == 'mon': - unit = 'weeks' - num *= 4 - elif unit == 'y': - unit = 'weeks' - num *= 52 - return timedelta(**{unit: num}) - - -def parse_int(val: str) -> tuple: - try: - val = int(val) - if val is not 0: - if val < 1: - order, op = 'desc', '<=' - val *= -1 - else: - order, op = 'asc', '>=' - val -= 1 - return val, order, op - except ValueError: - pass diff --git a/nxy/utils/__init__.py b/nxy/utils/__init__.py new file mode 100644 index 0000000..bea46b5 --- /dev/null +++ b/nxy/utils/__init__.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +from pprint import pprint as pp + +from .date import date_from_iso, time_delta, time_since +from .formatting import fmt, FORMATTING +from .misc import parse_int + +__all__ = ( + # pprint alias + 'pp', + # date and time stuff + 'date_from_iso', + 'time_delta', + 'time_since', + # formatting + 'fmt', + 'FORMATTING', + # misc + 'parse_int' +) diff --git a/nxy/utils/date.py b/nxy/utils/date.py new file mode 100644 index 0000000..67d21f8 --- /dev/null +++ b/nxy/utils/date.py @@ -0,0 +1,84 @@ +# -*- coding: utf-8 -*- +import re + +from datetime import datetime, timedelta + + +def date_from_iso(date: str) -> datetime: + return datetime.strptime(date, '%Y-%m-%dT%H:%M:%S.%fZ') + + +def time_delta(text: str) -> timedelta: + match = re.match(r'(\d+)(s|m|h|mon|w|y)', text) + if match: + num, unit = match.groups() + num = int(num) + if unit == 's': + unit = 'seconds' + elif unit == 'm': + unit = 'minutes' + elif unit == 'h': + unit = 'hours' + elif unit == 'd': + unit = 'days' + elif unit == 'w': + unit = 'weeks' + elif unit == 'mon': + unit = 'weeks' + num *= 4 + elif unit == 'y': + unit = 'weeks' + num *= 52 + return timedelta(**{unit: num}) + + +def time_since(date: datetime, now: datetime = None): + """ + Takes two datetime objects and returns the time between d and now + as a nicely formatted string, e.g. "10 minutes". If d occurs after now, + then "0 minutes" is returned. + + Units used are years, months, weeks, days, hours, and minutes. + Seconds and microseconds are ignored. Up to two adjacent units will be + displayed. For example, "2 weeks, 3 days" and "1 year, 3 months" are + possible outputs, but "2 weeks, 3 hours" and "1 year, 5 days" are not. + + Adapted from http://blog.natbat.co.uk/archive/2003/Jun/14/time_since + """ + chunks = ( + (60 * 60 * 24 * 365, ('year', 'years')), + (60 * 60 * 24 * 30, ('month', 'months')), + (60 * 60 * 24 * 7, ('week', 'weeks')), + (60 * 60 * 24, ('day', 'days')), + (60 * 60, ('hour', 'hours')), + (60, ('minute', 'minutes')) + ) + + if not now: + now = datetime.now() + + # ignore microsecond part of 'd' since we removed it from 'now' + delta = now - (date - timedelta(0, 0, date.microsecond)) + since = delta.days * 24 * 60 * 60 + delta.seconds + if since <= 0: + # d is in the future compared to now, stop processing. + return u'0 ' + 'minutes' + + name = None + count = None + seconds = None + + for i, (seconds, name) in enumerate(chunks): + count = since // seconds + if count != 0: + break + + s = '%(number)d %(type)s' % {'number': count, 'type': name[count != 1]} + + if i + 1 < len(chunks): + # Now get the second item + seconds2, name2 = chunks[i + 1] + count2 = (since - (seconds * count)) // seconds2 + if count2 != 0: + s += ', %d %s' % (count2, name2[count2 != 1]) + return s diff --git a/nxy/utils/formatting.py b/nxy/utils/formatting.py new file mode 100644 index 0000000..f8340da --- /dev/null +++ b/nxy/utils/formatting.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- + +# @formatter:off +COLORS = { + 'white': 0, # white + 'black': 1, # black + 'blue': 2, # blue (navy) + 'green': 3, # green + 'red': 4, # red + 'maroon': 5, # brown (maroon) + 'purple': 6, # purple + 'orange': 7, # orange (olive) + 'yellow': 8, # yellow + 'ltgreen': 9, # light green (lime) + 'teal': 10, # teal (a green/blue cyan) + 'ltcyan': 11, # light cyan (cyan / aqua) + 'ltblue': 12, # light blue (royal) + 'pink': 13, # pink (light purple / fuchsia) + 'grey': 14, # grey + 'ltgrey': 15, # light grey (silver) +} + +FORMATTING = { + 'bold': '\x02', # bold + 'color': '\x03', # colored text + 'italic': '\x1D', # italic text + 'underline': '\x1F', # underlined text + 'swap': '\x16', # swap bg and fg colors ("reverse video") + 'reset': '\x0F', # reset all formatting +} + +FORMATTING.update(COLORS) +# @formatter:on + + +def fmt(__text: str, **kwargs) -> str: + """Formats a str with `kwargs` and `FORMATTING`.""" + return __text.format(**kwargs, **FORMATTING) diff --git a/nxy/utils/misc.py b/nxy/utils/misc.py new file mode 100644 index 0000000..d66dbe6 --- /dev/null +++ b/nxy/utils/misc.py @@ -0,0 +1,17 @@ +# -*- coding: utf-8 -*- + + +def parse_int(val: str, select: bool = True) -> tuple: + try: + val = int(val) + if val is not 0: + if val < 1: + order, op = 'desc', '<=' + val *= -1 + else: + order, op = 'asc', '>=' + if select: + val -= 1 + return val, order, op + except ValueError: + pass