17
16
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20
from elementtree import ElementTree as ET
35
log = logging.getLogger("loggerhead.controllers")
39
# date_day -- just the day
40
# date_time -- full date with time
42
# displaydate -- for use in sentences
43
# approximatedate -- for use in tables
45
# displaydate and approximatedate return an elementtree <span> Element
46
# with the full date in a tooltip.
49
return value.strftime('%Y-%m-%d')
53
return value.strftime('%Y-%m-%d %T')
56
def _displaydate(date):
57
delta = abs(datetime.datetime.now() - date)
58
if delta > datetime.timedelta(1, 0, 0):
59
# far in the past or future, display the date
60
return 'on ' + date_day(date)
61
return _approximatedate(date)
64
def _approximatedate(date):
65
delta = datetime.datetime.now() - date
66
if abs(delta) > datetime.timedelta(1, 0, 0):
67
# far in the past or future, display the date
69
future = delta < datetime.timedelta(0, 0, 0)
72
hours = delta.seconds / 3600
73
minutes = (delta.seconds - (3600*hours)) / 60
74
seconds = delta.seconds % 60
92
result += '%s %s' % (amount, unit)
98
def _wrap_with_date_time_title(date, formatted_date):
99
elem = ET.Element("span")
100
elem.text = formatted_date
101
elem.set("title", date_time(date))
105
def approximatedate(date):
106
#FIXME: Returns an object instead of a string
107
return _wrap_with_date_time_title(date, _approximatedate(date))
110
def displaydate(date):
111
return _wrap_with_date_time_title(date, _displaydate(date))
25
return '%d days' % delta.days
31
seg.append('%d days' % delta.days)
32
hrs = delta.seconds // 3600
33
mins = (delta.seconds % 3600) // 60
38
seg.append('%d hours' % hrs)
42
seg.append('1 minute')
44
seg.append('%d minutes' % mins)
46
seg.append('less than a minute')
114
50
class Container (object):
211
125
out.insert(0, -n)
214
# only do this if unicode turns out to be a problem
215
#_BADCHARS_RE = re.compile(ur'[\u007f-\uffff]')
217
# FIXME: get rid of this method; use fixed_width() and avoid XML().
220
clean up a string for html display. expand any tabs, encode any html
221
entities, and replace spaces with ' '. this is primarily for use
222
in displaying monospace text.
224
s = cgi.escape(s.expandtabs())
225
s = s.replace(' ', ' ')
230
NONBREAKING_SPACE = u'\N{NO-BREAK SPACE}'
234
expand tabs and turn spaces into "non-breaking spaces", so browsers won't
237
if not isinstance(s, unicode):
238
# this kinda sucks. file contents are just binary data, and no
239
# encoding metadata is stored, so we need to guess. this is probably
240
# okay for most code, but for people using things like KOI-8, this
241
# will display gibberish. we have no way of detecting the correct
244
s = s.decode('utf-8')
245
except UnicodeDecodeError:
246
s = s.decode('iso-8859-15')
247
return s.expandtabs().replace(' ', NONBREAKING_SPACE)
250
def fake_permissions(kind, executable):
251
# fake up unix-style permissions given only a "kind" and executable bit
252
if kind == 'directory':
259
def if_present(format, value):
261
format a value using a format string, if the value exists and is not None.
265
return format % value
269
s = base64.encodestring(s).replace('\n', '')
270
while (len(s) > 0) and (s[-1] == '='):
277
turn a potentially long string into a unique smaller string.
281
uniqs[type(None)] = next = uniqs.get(type(None), 0) + 1
282
x = struct.pack('>I', next)
283
while (len(x) > 1) and (x[0] == '\x00'):
292
P95_MEG = int(0.9 * MEG)
293
P95_GIG = int(0.9 * GIG)
295
def human_size(size, min_divisor=0):
297
if (size == 0) and (min_divisor == 0):
299
if (size < 512) and (min_divisor == 0):
302
if (size >= P95_GIG) or (min_divisor >= GIG):
304
elif (size >= P95_MEG) or (min_divisor >= MEG):
311
dot = dot * 10 // divisor
318
if (base < 100) and (dot != 0):
319
out += '.%d' % (dot,)
329
def fill_in_navigation(navigation):
331
given a navigation block (used by the template for the page header), fill
332
in useful calculated values.
334
if navigation.revid in navigation.revid_list: # XXX is this always true?
335
navigation.position = navigation.revid_list.index(navigation.revid)
337
navigation.position = 0
338
navigation.count = len(navigation.revid_list)
339
navigation.page_position = navigation.position // navigation.pagesize + 1
340
navigation.page_count = (len(navigation.revid_list) + (navigation.pagesize - 1)) // navigation.pagesize
342
def get_offset(offset):
343
if (navigation.position + offset < 0) or (navigation.position + offset > navigation.count - 1):
345
return navigation.revid_list[navigation.position + offset]
347
navigation.prev_page_revid = get_offset(-1 * navigation.pagesize)
348
navigation.next_page_revid = get_offset(1 * navigation.pagesize)
350
params = { 'filter_file_id': navigation.filter_file_id }
351
if getattr(navigation, 'query', None) is not None:
352
params['q'] = navigation.query
354
params['start_revid'] = navigation.start_revid
356
if navigation.prev_page_revid:
357
navigation.prev_page_url = navigation.branch.url([ navigation.scan_url, navigation.prev_page_revid ], **get_context(**params))
358
if navigation.next_page_revid:
359
navigation.next_page_url = navigation.branch.url([ navigation.scan_url, navigation.next_page_revid ], **get_context(**params))
362
def log_exception(log):
363
for line in ''.join(traceback.format_exception(*sys.exc_info())).split('\n'):
367
def decorator(unbound):
368
def new_decorator(f):
370
g.__name__ = f.__name__
371
g.__doc__ = f.__doc__
372
g.__dict__.update(f.__dict__)
374
new_decorator.__name__ = unbound.__name__
375
new_decorator.__doc__ = unbound.__doc__
376
new_decorator.__dict__.update(unbound.__dict__)
380
# common threading-lock decorator
381
def with_lock(lockname, debug_name=None):
382
if debug_name is None:
383
debug_name = lockname
385
def _decorator(unbound):
386
def locked(self, *args, **kw):
387
getattr(self, lockname).acquire()
389
return unbound(self, *args, **kw)
391
getattr(self, lockname).release()
397
def strip_whitespace(f):
401
out = re.sub(r'\n\s+', '\n', out)
402
out = re.sub(r'[ \t]+', ' ', out)
403
out = re.sub(r'\s+\n', '\n', out)
405
log.debug('Saved %sB (%d%%) by stripping whitespace.',
406
human_size(orig_len - new_len),
407
round(100.0 - float(new_len) * 100.0 / float(orig_len)))
415
from loggerhead.lsprof import profile
418
ret, stats = profile(f, *a, **kw)
419
log.debug('Finished profiled %s in %d msec.' % (f.__name__, int((time.time() - z) * 1000)))
423
msec = int(now * 1000) % 1000
424
timestr = time.strftime('%Y%m%d%H%M%S', time.localtime(now)) + ('%03d' % msec)
425
filename = f.__name__ + '-' + timestr + '.lsprof'
426
cPickle.dump(stats, open(filename, 'w'), 2)
431
# just thinking out loud here...
433
# so, when browsing around, there are 5 pieces of context, most optional:
435
# current location along the navigation path (while browsing)
436
# - starting revid (start_revid)
437
# the current beginning of navigation (navigation continues back to
438
# the original revision) -- this defines an 'alternate mainline'
439
# when the user navigates into a branch.
441
# the file being looked at
443
# if navigating the revisions that touched a file
445
# if navigating the revisions that matched a search query
447
# a previous revision to remember for future comparisons
449
# current revid is given on the url path. the rest are optional components
452
# other transient things can be set:
454
# to compare one revision to another, on /revision only
456
# for re-ordering an existing page by different sort
458
t_context = threading.local()
459
_valid = ('start_revid', 'file_id', 'filter_file_id', 'q', 'remember',
460
'compare_revid', 'sort')
463
def set_context(map):
464
t_context.map = dict((k, v) for (k, v) in map.iteritems() if k in _valid)
467
def get_context(**overrides):
469
return a context map that may be overriden by specific values passed in,
470
but only contains keys from the list of valid context keys.
472
if 'clear' is set, only the 'remember' context value will be added, and
473
all other context will be omitted.
476
if overrides.get('clear', False):
477
map['remember'] = t_context.map.get('remember', None)
479
map.update(t_context.map)
480
overrides = dict((k, v) for (k, v) in overrides.iteritems() if k in _valid)
481
map.update(overrides)