Major refactor, package cleanup. Untested.
This commit is contained in:
127
rtv/helpers.py
Normal file
127
rtv/helpers.py
Normal file
@@ -0,0 +1,127 @@
|
||||
import sys
|
||||
import os
|
||||
import textwrap
|
||||
import subprocess
|
||||
from datetime import datetime
|
||||
|
||||
from . import config
|
||||
|
||||
__all__ = ['open_browser', 'clean', 'wrap_text', 'strip_textpad',
|
||||
'strip_subreddit_url', 'humanize_timestamp']
|
||||
|
||||
def open_browser(url):
|
||||
"""
|
||||
Call webbrowser.open_new_tab(url) and redirect stdout/stderr to devnull.
|
||||
|
||||
This is a workaround to stop firefox from spewing warning messages to the
|
||||
console. See http://bugs.python.org/issue22277 for a better description
|
||||
of the problem.
|
||||
"""
|
||||
command = "import webbrowser; webbrowser.open_new_tab('%s')" % url
|
||||
args = [sys.executable, '-c', command]
|
||||
with open(os.devnull, 'ab+', 0) as null:
|
||||
subprocess.check_call(args, stdout=null, stderr=null)
|
||||
|
||||
def clean(string):
|
||||
"""
|
||||
Required reading!
|
||||
http://nedbatchelder.com/text/unipain.html
|
||||
|
||||
Python 2 input string will be a unicode type (unicode code points). Curses
|
||||
will accept unicode if all of the points are in the ascii range. However, if
|
||||
any of the code points are not valid ascii curses will throw a
|
||||
UnicodeEncodeError: 'ascii' codec can't encode character, ordinal not in
|
||||
range(128). If we encode the unicode to a utf-8 byte string and pass that to
|
||||
curses, it will render correctly.
|
||||
|
||||
Python 3 input string will be a string type (unicode code points). Curses
|
||||
will accept that in all cases. However, the n character count in addnstr
|
||||
will not be correct. If code points are passed to addnstr, curses will treat
|
||||
each code point as one character and will not account for wide characters.
|
||||
If utf-8 is passed in, addnstr will treat each 'byte' as a single character.
|
||||
"""
|
||||
|
||||
encoding = 'utf-8' if config.unicode else 'ascii'
|
||||
string = string.encode(encoding, 'replace')
|
||||
return string
|
||||
|
||||
def wrap_text(text, width):
|
||||
"""
|
||||
Wrap text paragraphs to the given character width while preserving newlines.
|
||||
"""
|
||||
out = []
|
||||
for paragraph in text.splitlines():
|
||||
# Wrap returns an empty list when paragraph is a newline. In order to
|
||||
# preserve newlines we substitute a list containing an empty string.
|
||||
lines = textwrap.wrap(paragraph, width=width) or ['']
|
||||
out.extend(lines)
|
||||
return out
|
||||
|
||||
def strip_textpad(text):
|
||||
"""
|
||||
Attempt to intelligently strip excess whitespace from the output of a
|
||||
curses textpad.
|
||||
"""
|
||||
|
||||
if text is None:
|
||||
return text
|
||||
|
||||
# Trivial case where the textbox is only one line long.
|
||||
if '\n' not in text:
|
||||
return text.rstrip()
|
||||
|
||||
# Allow one space at the end of the line. If there is more than one space,
|
||||
# assume that a newline operation was intended by the user
|
||||
stack, current_line = [], ''
|
||||
for line in text.split('\n'):
|
||||
if line.endswith(' '):
|
||||
stack.append(current_line + line.rstrip())
|
||||
current_line = ''
|
||||
else:
|
||||
current_line += line
|
||||
stack.append(current_line)
|
||||
|
||||
# Prune empty lines at the bottom of the textbox.
|
||||
for item in stack[::-1]:
|
||||
if len(item) == 0:
|
||||
stack.pop()
|
||||
else:
|
||||
break
|
||||
|
||||
out = '\n'.join(stack)
|
||||
return out
|
||||
|
||||
def strip_subreddit_url(permalink):
|
||||
"""
|
||||
Strip a subreddit name from the subreddit's permalink.
|
||||
|
||||
This is used to avoid submission.subreddit.url making a seperate API call.
|
||||
"""
|
||||
|
||||
subreddit = permalink.split('/')[4]
|
||||
return '/r/{}'.format(subreddit)
|
||||
|
||||
def humanize_timestamp(utc_timestamp, verbose=False):
|
||||
"""
|
||||
Convert a utc timestamp into a human readable relative-time.
|
||||
"""
|
||||
|
||||
timedelta = datetime.utcnow() - datetime.utcfromtimestamp(utc_timestamp)
|
||||
|
||||
seconds = int(timedelta.total_seconds())
|
||||
if seconds < 60:
|
||||
return 'moments ago' if verbose else '0min'
|
||||
minutes = seconds // 60
|
||||
if minutes < 60:
|
||||
return ('%d minutes ago' % minutes) if verbose else ('%dmin' % minutes)
|
||||
hours = minutes // 60
|
||||
if hours < 24:
|
||||
return ('%d hours ago' % hours) if verbose else ('%dhr' % hours)
|
||||
days = hours // 24
|
||||
if days < 30:
|
||||
return ('%d days ago' % days) if verbose else ('%dday' % days)
|
||||
months = days // 30.4
|
||||
if months < 12:
|
||||
return ('%d months ago' % months) if verbose else ('%dmonth' % months)
|
||||
years = months // 12
|
||||
return ('%d years ago' % years) if verbose else ('%dyr' % years)
|
||||
Reference in New Issue
Block a user