mirror of
https://github.com/samuelclay/NewsBlur.git
synced 2025-04-13 09:42:01 +00:00
179 lines
No EOL
6.1 KiB
Python
179 lines
No EOL
6.1 KiB
Python
import datetime
|
|
import threading
|
|
import sys
|
|
import traceback
|
|
import pprint
|
|
from django.core.mail import mail_admins
|
|
from django.utils.translation import ungettext
|
|
from utils import log as logging
|
|
|
|
class TimeoutError(Exception): pass
|
|
def timelimit(timeout):
|
|
"""borrowed from web.py"""
|
|
def _1(function):
|
|
def _2(*args, **kw):
|
|
class Dispatch(threading.Thread):
|
|
def __init__(self):
|
|
threading.Thread.__init__(self)
|
|
self.result = None
|
|
self.error = None
|
|
|
|
self.setDaemon(True)
|
|
self.start()
|
|
|
|
def run(self):
|
|
try:
|
|
self.result = function(*args, **kw)
|
|
except:
|
|
self.error = sys.exc_info()
|
|
|
|
c = Dispatch()
|
|
c.join(timeout)
|
|
if c.isAlive():
|
|
raise TimeoutError, 'took too long'
|
|
if c.error:
|
|
tb = ''.join(traceback.format_exception(c.error[0], c.error[1], c.error[2]))
|
|
logging.debug(tb)
|
|
mail_admins('Error in timeout: %s' % c.error[0], tb)
|
|
raise c.error[0], c.error[1]
|
|
return c.result
|
|
return _2
|
|
return _1
|
|
|
|
def utf8encode(tstr):
|
|
""" Encodes a unicode string in utf-8
|
|
"""
|
|
if not tstr:
|
|
return ''
|
|
# this is _not_ pretty, but it works
|
|
try:
|
|
return tstr.encode('utf-8', "xmlcharrefreplace")
|
|
except UnicodeDecodeError:
|
|
# it's already UTF8.. sigh
|
|
try:
|
|
return tstr.decode('utf-8').encode('utf-8')
|
|
except UnicodeDecodeError:
|
|
return ''
|
|
|
|
# From: http://www.poromenos.org/node/87
|
|
def levenshtein_distance(first, second):
|
|
"""Find the Levenshtein distance between two strings."""
|
|
if len(first) > len(second):
|
|
first, second = second, first
|
|
if len(second) == 0:
|
|
return len(first)
|
|
first_length = len(first) + 1
|
|
second_length = len(second) + 1
|
|
distance_matrix = [[0] * second_length for x in range(first_length)]
|
|
for i in range(first_length):
|
|
distance_matrix[i][0] = i
|
|
for j in range(second_length):
|
|
distance_matrix[0][j]=j
|
|
for i in xrange(1, first_length):
|
|
for j in range(1, second_length):
|
|
deletion = distance_matrix[i-1][j] + 1
|
|
insertion = distance_matrix[i][j-1] + 1
|
|
substitution = distance_matrix[i-1][j-1]
|
|
if first[i-1] != second[j-1]:
|
|
substitution += 1
|
|
distance_matrix[i][j] = min(insertion, deletion, substitution)
|
|
return distance_matrix[first_length-1][second_length-1]
|
|
|
|
def _do_timesince(d, chunks, now=None):
|
|
"""
|
|
Started as a copy of django.util.timesince.timesince, but modified to
|
|
only output one time unit, and use months as the maximum unit of measure.
|
|
|
|
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 months, weeks, days, hours, and minutes.
|
|
Seconds and microseconds are ignored.
|
|
"""
|
|
# Convert datetime.date to datetime.datetime for comparison
|
|
if d.__class__ is not datetime.datetime:
|
|
d = datetime.datetime(d.year, d.month, d.day)
|
|
|
|
if not now:
|
|
now = datetime.datetime.utcnow()
|
|
|
|
# ignore microsecond part of 'd' since we removed it from 'now'
|
|
delta = now - (d - datetime.timedelta(0, 0, d.microsecond))
|
|
since = delta.days * 24 * 60 * 60 + delta.seconds
|
|
for i, (seconds, name) in enumerate(chunks):
|
|
count = since // seconds
|
|
if count != 0:
|
|
break
|
|
s = '%(number)d %(type)s' % {'number': count, 'type': name(count)}
|
|
return s
|
|
|
|
def relative_timesince(value):
|
|
if not value:
|
|
return u''
|
|
|
|
chunks = (
|
|
(60 * 60, lambda n: ungettext('hour', 'hours', n)),
|
|
(60, lambda n: ungettext('minute', 'minutes', n))
|
|
)
|
|
return _do_timesince(value, chunks)
|
|
|
|
def relative_timeuntil(value):
|
|
if not value:
|
|
return u''
|
|
|
|
chunks = (
|
|
(60 * 60, lambda n: ungettext('hour', 'hours', n)),
|
|
(60, lambda n: ungettext('minute', 'minutes', n))
|
|
)
|
|
|
|
now = datetime.datetime.utcnow()
|
|
|
|
return _do_timesince(now, chunks, value)
|
|
|
|
def format_relative_date(date, future=False):
|
|
if not date or date < datetime.datetime(2010, 1, 1):
|
|
return "Soon"
|
|
|
|
now = datetime.datetime.utcnow()
|
|
diff = abs(now - date)
|
|
if diff < datetime.timedelta(minutes=60):
|
|
minutes = diff.seconds / 60
|
|
return "%s minute%s %s" % (minutes,
|
|
'' if minutes == 1 else 's',
|
|
'' if future else 'ago')
|
|
elif datetime.timedelta(minutes=60) <= diff < datetime.timedelta(minutes=90):
|
|
return "1 hour %s" % ('' if future else 'ago')
|
|
elif diff >= datetime.timedelta(minutes=90):
|
|
dec = (diff.seconds / 60 + 15) % 60
|
|
if dec >= 30:
|
|
return "%s.5 hours %s" % ((((diff.seconds / 60) + 15) / 60),
|
|
'' if future else 'ago')
|
|
else:
|
|
return "%s hours %s" % ((((diff.seconds / 60) + 15) / 60),
|
|
'' if future else 'ago')
|
|
|
|
def add_object_to_folder(obj, folder, folders, added=False):
|
|
if not folder and obj not in folders:
|
|
folders.append(obj)
|
|
return folders
|
|
|
|
for k, v in enumerate(folders):
|
|
if isinstance(v, dict):
|
|
for f_k, f_v in v.items():
|
|
if f_k == folder and obj not in f_v and not added:
|
|
f_v.append(obj)
|
|
added = True
|
|
folders[k][f_k] = add_object_to_folder(obj, folder, f_v, added)
|
|
return folders
|
|
|
|
def mail_feed_error_to_admin(feed, e):
|
|
# Mail the admins with the error
|
|
exc_info = sys.exc_info()
|
|
subject = 'Feed update error: %s' % repr(e)
|
|
message = 'Traceback:\n%s\n\Feed:\n%s' % (
|
|
'\n'.join(traceback.format_exception(*exc_info)),
|
|
pprint.pformat(feed.__dict__)
|
|
)
|
|
# print message
|
|
mail_admins(subject, message) |