2016-05-10 16:14:53 +00:00
|
|
|
#!/usr/bin/python
|
|
|
|
|
2016-05-22 02:18:25 +00:00
|
|
|
'''
|
|
|
|
util.py: frequently used terminal and text processing utilities
|
|
|
|
copyright (c) 2016 ~endorphant (endorphant@tilde.town)
|
|
|
|
|
2016-06-15 01:51:49 +00:00
|
|
|
Permission is hereby granted, free of charge, to any person obtaining
|
|
|
|
a copy of this software and associated documentation files (the
|
|
|
|
"Software"), to deal in the Software without restriction, including
|
|
|
|
without limitation the rights to use, copy, modify, merge, publish,
|
|
|
|
distribute, sublicense, and/or sell copies of the Software, and to
|
|
|
|
permit persons to whom the Software is furnished to do so, subject to
|
|
|
|
the following conditions:
|
2016-05-22 02:18:25 +00:00
|
|
|
|
2016-06-15 01:51:49 +00:00
|
|
|
The above copyright notice and this permission notice shall be
|
|
|
|
included in all copies or substantial portions of the Software.
|
2016-05-22 02:18:25 +00:00
|
|
|
|
2016-06-15 01:51:49 +00:00
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
|
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
|
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
|
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
|
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
|
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
|
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
2016-05-22 02:18:25 +00:00
|
|
|
'''
|
2016-05-15 03:44:03 +00:00
|
|
|
import random
|
2017-11-21 06:02:10 +00:00
|
|
|
import time
|
2018-02-23 20:28:25 +00:00
|
|
|
from six.moves import input
|
2018-03-16 02:41:22 +00:00
|
|
|
import os
|
2017-11-21 06:02:10 +00:00
|
|
|
|
2016-05-15 04:19:05 +00:00
|
|
|
import colorama
|
2017-11-21 06:02:10 +00:00
|
|
|
import inflect
|
2016-05-15 04:19:05 +00:00
|
|
|
|
2016-05-27 07:21:11 +00:00
|
|
|
## misc globals
|
2018-03-15 20:17:21 +00:00
|
|
|
BACKS = ['back', 'b', 'q', '<q>']
|
2016-10-06 15:38:14 +00:00
|
|
|
NAVS = ['u', 'd']
|
2016-05-15 04:19:05 +00:00
|
|
|
|
2016-05-27 07:21:11 +00:00
|
|
|
## color stuff
|
|
|
|
colorama.init()
|
2016-05-15 04:19:05 +00:00
|
|
|
textcolors = [ colorama.Fore.RED, colorama.Fore.GREEN, colorama.Fore.YELLOW, colorama.Fore.BLUE, colorama.Fore.MAGENTA, colorama.Fore.WHITE, colorama.Fore.CYAN]
|
|
|
|
lastcolor = colorama.Fore.RESET
|
2016-05-10 16:14:53 +00:00
|
|
|
|
|
|
|
p = inflect.engine()
|
|
|
|
|
2016-05-15 04:19:05 +00:00
|
|
|
def set_rainbow():
|
2016-05-22 03:36:21 +00:00
|
|
|
'''
|
|
|
|
prints a random terminal color code
|
|
|
|
'''
|
|
|
|
|
2016-05-15 04:19:05 +00:00
|
|
|
global lastcolor
|
|
|
|
|
|
|
|
color = lastcolor
|
|
|
|
while color == lastcolor:
|
|
|
|
color = random.choice(textcolors)
|
|
|
|
|
|
|
|
lastcolor = color
|
|
|
|
|
|
|
|
print(color)
|
|
|
|
|
|
|
|
def reset_color():
|
2016-05-22 03:36:21 +00:00
|
|
|
'''
|
|
|
|
prints terminal color code reset
|
|
|
|
'''
|
|
|
|
|
2016-05-15 04:19:05 +00:00
|
|
|
print(colorama.Fore.RESET)
|
|
|
|
|
|
|
|
def attach_rainbow():
|
2016-05-22 03:36:21 +00:00
|
|
|
'''
|
|
|
|
returns a random terminal color code, presumably to be 'attached' to a string
|
|
|
|
'''
|
|
|
|
|
2016-05-15 04:19:05 +00:00
|
|
|
global lastcolor
|
|
|
|
|
|
|
|
color = lastcolor
|
|
|
|
while color == lastcolor:
|
|
|
|
color = random.choice(textcolors)
|
|
|
|
|
|
|
|
lastcolor = color
|
|
|
|
return color
|
|
|
|
|
|
|
|
def attach_reset():
|
2016-05-22 03:36:21 +00:00
|
|
|
'''
|
|
|
|
returns terminal color code reset, presumably to be 'attached' to a string
|
|
|
|
'''
|
|
|
|
|
2016-05-15 04:19:05 +00:00
|
|
|
return colorama.Style.RESET_ALL
|
|
|
|
|
|
|
|
def hilight(text):
|
2016-05-22 03:36:21 +00:00
|
|
|
'''
|
|
|
|
takes a string and highlights it on return
|
|
|
|
'''
|
|
|
|
|
2016-05-15 04:19:05 +00:00
|
|
|
return colorama.Style.BRIGHT+text+colorama.Style.NORMAL
|
|
|
|
|
2016-05-27 18:08:45 +00:00
|
|
|
def rainbow(txt):
|
|
|
|
'''
|
|
|
|
Takes a string and makes every letter a different color.
|
|
|
|
'''
|
|
|
|
|
|
|
|
rainbow = ""
|
|
|
|
for letter in txt:
|
|
|
|
rainbow += attach_rainbow() + letter
|
|
|
|
|
|
|
|
rainbow += attach_reset()
|
|
|
|
|
|
|
|
return rainbow
|
|
|
|
|
2016-05-10 16:14:53 +00:00
|
|
|
def pretty_time(time):
|
2016-05-22 03:36:21 +00:00
|
|
|
'''
|
|
|
|
human-friendly time formatter
|
|
|
|
|
|
|
|
takes an integer number of seconds and returns a phrase that describes it,
|
|
|
|
using the largest possible figure, rounded down (ie, time=604 returns '10
|
|
|
|
minutes', not '10 minutes, 4 seconds' or '604 seconds')
|
|
|
|
'''
|
|
|
|
|
2016-05-10 16:14:53 +00:00
|
|
|
m, s = divmod(time, 60)
|
|
|
|
if m > 0:
|
|
|
|
h, m = divmod(m, 60)
|
|
|
|
if h > 0:
|
|
|
|
d, h = divmod(h, 24)
|
|
|
|
if d > 0:
|
|
|
|
w, d = divmod(d, 7)
|
|
|
|
if w > 0:
|
|
|
|
mo, w = divmod(w, 4)
|
|
|
|
if mo > 0:
|
|
|
|
return p.no("month", mo)
|
|
|
|
else:
|
|
|
|
return p.no("week", w)
|
|
|
|
else:
|
|
|
|
return p.no("day", d)
|
|
|
|
else:
|
|
|
|
return p.no("hour", h)
|
|
|
|
else:
|
|
|
|
return p.no("minute", m)
|
|
|
|
else:
|
|
|
|
return p.no("second", s)
|
2016-05-15 03:44:03 +00:00
|
|
|
|
|
|
|
def genID(digits=5):
|
2016-05-22 03:36:21 +00:00
|
|
|
'''
|
|
|
|
returns a string-friendly string of digits, which can start with 0
|
|
|
|
'''
|
2016-05-15 03:44:03 +00:00
|
|
|
|
|
|
|
id = ""
|
|
|
|
x = 0
|
|
|
|
while x < digits:
|
|
|
|
id += str(random.randint(0,9))
|
|
|
|
x += 1
|
|
|
|
|
|
|
|
return id
|
2016-05-27 07:21:11 +00:00
|
|
|
|
|
|
|
def print_menu(menu, rainbow=False):
|
|
|
|
'''
|
|
|
|
A pretty menu handler that takes an incoming lists of
|
|
|
|
options and prints them nicely.
|
|
|
|
|
|
|
|
Set rainbow=True for colorized menus.
|
|
|
|
'''
|
|
|
|
|
|
|
|
i = 0
|
|
|
|
for x in menu:
|
|
|
|
line = []
|
2016-11-08 23:38:15 +00:00
|
|
|
if rainbow is not False:
|
2016-05-27 07:21:11 +00:00
|
|
|
line.append(attach_rainbow())
|
|
|
|
line.append("\t[ ")
|
|
|
|
if i < 10:
|
|
|
|
line.append(" ")
|
|
|
|
line.append(str(i)+" ] "+x)
|
|
|
|
line.append(attach_reset())
|
|
|
|
print("".join(line))
|
|
|
|
i += 1
|
|
|
|
|
|
|
|
def list_select(options, prompt):
|
|
|
|
'''
|
|
|
|
Given a list and query prompt, returns either False as an
|
|
|
|
eject flag, or an integer index of the list Catches cancel
|
|
|
|
option from list defined by BACKS; otherwise, retries on
|
|
|
|
ValueError or IndexError.
|
|
|
|
'''
|
|
|
|
|
|
|
|
ans = ""
|
|
|
|
invalid = True
|
|
|
|
|
2018-02-23 20:28:25 +00:00
|
|
|
choice = input("\n"+prompt)
|
2016-05-27 07:21:11 +00:00
|
|
|
|
|
|
|
if choice in BACKS:
|
|
|
|
return False
|
|
|
|
|
2016-10-06 15:38:14 +00:00
|
|
|
if choice in NAVS:
|
|
|
|
return choice
|
|
|
|
|
2016-05-27 07:21:11 +00:00
|
|
|
try:
|
|
|
|
ans = int(choice)
|
|
|
|
except ValueError:
|
|
|
|
return list_select(options, prompt)
|
|
|
|
|
|
|
|
try:
|
|
|
|
options[ans]
|
|
|
|
except IndexError:
|
|
|
|
return list_select(options, prompt)
|
|
|
|
|
|
|
|
return ans
|
|
|
|
|
|
|
|
def input_yn(query):
|
|
|
|
'''
|
|
|
|
Given a query, returns boolean True or False by processing y/n input
|
|
|
|
'''
|
|
|
|
|
|
|
|
try:
|
2018-02-23 20:28:25 +00:00
|
|
|
ans = input(query+" [y/n] ")
|
2016-05-27 07:21:11 +00:00
|
|
|
except KeyboardInterrupt:
|
|
|
|
input_yn(query)
|
|
|
|
|
|
|
|
while ans not in ["y", "n"]:
|
2018-02-23 20:28:25 +00:00
|
|
|
ans = input("'y' or 'n' please: ")
|
2016-05-27 07:21:11 +00:00
|
|
|
|
2017-12-04 04:05:04 +00:00
|
|
|
return ans == "y"
|
2018-03-16 02:41:22 +00:00
|
|
|
|
|
|
|
def parse_date(file):
|
|
|
|
'''
|
|
|
|
parses date out of pre-validated filename
|
|
|
|
|
|
|
|
* assumes a filename of YYYYMMDD.txt
|
|
|
|
* returns a list:
|
|
|
|
[0] 'YYYY'
|
|
|
|
[1] 'MM'
|
|
|
|
[2] 'DD'
|
|
|
|
'''
|
|
|
|
|
|
|
|
rawdate = os.path.splitext(os.path.basename(file))[0]
|
|
|
|
|
|
|
|
date = [rawdate[0:4], rawdate[4:6], rawdate[6:]]
|
|
|
|
|
|
|
|
return date
|
|
|
|
|