19
17
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23
from xml.etree import ElementTree as ET
25
from elementtree import ElementTree as ET
37
33
log = logging.getLogger("loggerhead.controllers")
39
return '%d years' % (int(delta.days // 365.25),)
41
return '%d days' % delta.days
47
seg.append('%d days' % delta.days)
48
hrs = delta.seconds // 3600
49
mins = (delta.seconds % 3600) // 60
54
seg.append('%d hours' % hrs)
58
seg.append('1 minute')
60
seg.append('%d minutes' % mins)
62
seg.append('less than a minute')
67
now = datetime.datetime.now()
68
return timespan(now - timestamp) + ' ago'
39
71
def fix_year(year):
48
# date_day -- just the day
49
# date_time -- full date with time
51
# displaydate -- for use in sentences
52
# approximatedate -- for use in tables
54
# displaydate and approximatedate return an elementtree <span> Element
55
# with the full date in a tooltip.
58
return value.strftime('%Y-%m-%d')
63
return value.strftime('%Y-%m-%d %T')
68
def _displaydate(date):
69
delta = abs(datetime.datetime.now() - date)
70
if delta > datetime.timedelta(1, 0, 0):
71
# far in the past or future, display the date
72
return 'on ' + date_day(date)
73
return _approximatedate(date)
76
def _approximatedate(date):
79
_g_format = '%Y-%m-%d @ %H:%M'
81
def format_date(date):
82
if _g_format == 'fancy':
83
return fancy_format_date(date)
84
return date.strftime(_g_format)
86
def fancy_format_date(date):
77
87
delta = datetime.datetime.now() - date
78
if abs(delta) > datetime.timedelta(1, 0, 0):
79
# far in the past or future, display the date
81
future = delta < datetime.timedelta(0, 0, 0)
84
hours = delta.seconds / 3600
85
minutes = (delta.seconds - (3600*hours)) / 60
86
seconds = delta.seconds % 60
89
return date.strftime('%d %b %Y')
104
result += '%s %s' % (amount, unit)
110
def _wrap_with_date_time_title(date, formatted_date):
111
elem = ET.Element("span")
112
elem.text = formatted_date
113
elem.set("title", date_time(date))
117
def approximatedate(date):
118
#FIXME: Returns an object instead of a string
119
return _wrap_with_date_time_title(date, _approximatedate(date))
122
def displaydate(date):
123
return _wrap_with_date_time_title(date, _displaydate(date))
91
return date.strftime('%d %b %H:%M')
93
def set_date_format(format):
126
98
class Container (object):
119
def clean_revid(revid):
120
if revid == 'missing':
122
return sha.new(revid).hexdigest()
126
return ''.join([ '&#%d;' % ord(c) for c in text ])
147
129
def trunc(text, limit=10):
148
130
if len(text) <= limit:
150
132
return text[:limit] + '...'
136
if isinstance(s, unicode):
137
return s.encode('utf-8')
153
141
STANDARD_PATTERN = re.compile(r'^(.*?)\s*<(.*?)>\s*$')
154
142
EMAIL_PATTERN = re.compile(r'[-\w\d\+_!%\.]+@[-\w\d\+_!%\.]+')
173
161
return '%s at %s' % (username, domains[0])
164
def triple_factors(min_value=1):
170
yield n * factors[index]
172
if index >= len(factors):
177
def scan_range(pos, max, pagesize=1):
179
given a position in a maximum range, return a list of negative and positive
180
jump factors for an hgweb-style triple-factor geometric scan.
182
for example, with pos=20 and max=500, the range would be:
183
[ -10, -3, -1, 1, 3, 10, 30, 100, 300 ]
185
i admit this is a very strange way of jumping through revisions. i didn't
189
for n in triple_factors(pagesize + 1):
176
198
# only do this if unicode turns out to be a problem
177
199
#_BADCHARS_RE = re.compile(ur'[\u007f-\uffff]')
187
209
s = s.replace(' ', ' ')
190
214
NONBREAKING_SPACE = u'\N{NO-BREAK SPACE}'
194
CSS is stupid. In some cases we need to replace an empty value with
195
a non breaking space ( ). There has to be a better way of doing this.
197
return: the same value recieved if not empty, and a ' ' if it is.
203
elif isinstance(s, int):
209
s = s.decode('utf-8')
210
except UnicodeDecodeError:
211
s = s.decode('iso-8859-15')
215
216
def fixed_width(s):
217
218
expand tabs and turn spaces into "non-breaking spaces", so browsers won't
239
240
return '-rw-r--r--'
243
def if_present(format, value):
245
format a value using a format string, if the value exists and is not None.
249
return format % value
243
253
s = base64.encodestring(s).replace('\n', '')
244
254
while (len(s) > 0) and (s[-1] == '='):
319
329
return navigation.revid_list[navigation.position + offset]
321
navigation.last_in_page_revid = get_offset(navigation.pagesize - 1)
322
331
navigation.prev_page_revid = get_offset(-1 * navigation.pagesize)
323
332
navigation.next_page_revid = get_offset(1 * navigation.pagesize)
324
prev_page_revno = navigation.history.get_revno(
325
navigation.prev_page_revid)
326
next_page_revno = navigation.history.get_revno(
327
navigation.next_page_revid)
328
start_revno = navigation.history.get_revno(navigation.start_revid)
330
params = { 'filter_file_id': navigation.filter_file_id }
334
params = { 'file_id': navigation.file_id }
331
335
if getattr(navigation, 'query', None) is not None:
332
336
params['q'] = navigation.query
334
if getattr(navigation, 'start_revid', None) is not None:
335
params['start_revid'] = start_revno
338
params['start_revid'] = navigation.start_revid
337
340
if navigation.prev_page_revid:
338
navigation.prev_page_url = navigation.branch.context_url(
339
[navigation.scan_url, prev_page_revno], **params)
341
navigation.prev_page_url = navigation.branch.url([ navigation.scan_url, navigation.prev_page_revid ], **get_context(**params))
340
342
if navigation.next_page_revid:
341
navigation.next_page_url = navigation.branch.context_url(
342
[navigation.scan_url, next_page_revno], **params)
343
navigation.next_page_url = navigation.branch.url([ navigation.scan_url, navigation.next_page_revid ], **get_context(**params))
346
def log_exception(log):
347
for line in ''.join(traceback.format_exception(*sys.exc_info())).split('\n'):
345
351
def decorator(unbound):
381
def strip_whitespace(f):
385
out = re.sub(r'\n\s+', '\n', out)
386
out = re.sub(r'[ \t]+', ' ', out)
387
out = re.sub(r'\s+\n', '\n', out)
389
log.debug('Saved %sB (%d%%) by stripping whitespace.',
390
human_size(orig_len - new_len),
391
round(100.0 - float(new_len) * 100.0 / float(orig_len)))
376
398
def _f(*a, **kw):
377
399
from loggerhead.lsprof import profile
397
419
# current location along the navigation path (while browsing)
398
420
# - starting revid (start_revid)
399
421
# the current beginning of navigation (navigation continues back to
400
# the original revision) -- this defines an 'alternate mainline'
401
# when the user navigates into a branch.
422
# the original revision) -- this may not be along the primary revision
423
# path since the user may have navigated into a branch
403
# the file being looked at
405
425
# if navigating the revisions that touched a file
407
427
# if navigating the revisions that matched a search query
418
438
# for re-ordering an existing page by different sort
420
440
t_context = threading.local()
421
_valid = ('start_revid', 'file_id', 'filter_file_id', 'q', 'remember',
422
'compare_revid', 'sort')
441
_valid = ('start_revid', 'file_id', 'q', 'remember', 'compare_revid', 'sort')
425
444
def set_context(map):