17
19
# 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
33
37
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'
71
39
def fix_year(year):
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):
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')
62
return value.strftime('%Y-%m-%d %T')
65
def _displaydate(date):
66
delta = abs(datetime.datetime.now() - date)
67
if delta > datetime.timedelta(1, 0, 0):
68
# far in the past or future, display the date
69
return 'on ' + date_day(date)
70
return _approximatedate(date)
73
def _approximatedate(date):
87
74
delta = datetime.datetime.now() - date
89
return date.strftime('%d %b %Y')
75
if abs(delta) > datetime.timedelta(1, 0, 0):
76
# far in the past or future, display the date
78
future = delta < datetime.timedelta(0, 0, 0)
81
hours = delta.seconds / 3600
82
minutes = (delta.seconds - (3600*hours)) / 60
83
seconds = delta.seconds % 60
91
return date.strftime('%d %b %H:%M')
93
def set_date_format(format):
101
result += '%s %s' % (amount, unit)
107
def _wrap_with_date_time_title(date, formatted_date):
108
elem = ET.Element("span")
109
elem.text = formatted_date
110
elem.set("title", date_time(date))
114
def approximatedate(date):
115
#FIXME: Returns an object instead of a string
116
return _wrap_with_date_time_title(date, _approximatedate(date))
119
def displaydate(date):
120
return _wrap_with_date_time_title(date, _displaydate(date))
98
123
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 ])
129
144
def trunc(text, limit=10):
130
145
if len(text) <= limit:
132
147
return text[:limit] + '...'
136
if isinstance(s, unicode):
137
return s.encode('utf-8')
141
150
STANDARD_PATTERN = re.compile(r'^(.*?)\s*<(.*?)>\s*$')
142
151
EMAIL_PATTERN = re.compile(r'[-\w\d\+_!%\.]+@[-\w\d\+_!%\.]+')
161
170
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):
198
173
# only do this if unicode turns out to be a problem
199
174
#_BADCHARS_RE = re.compile(ur'[\u007f-\uffff]')
209
184
s = s.replace(' ', ' ')
214
187
NONBREAKING_SPACE = u'\N{NO-BREAK SPACE}'
191
CSS is stupid. In some cases we need to replace an empty value with
192
a non breaking space ( ). There has to be a better way of doing this.
194
return: the same value recieved if not empty, and a ' ' if it is.
200
elif isinstance(s, int):
206
s = s.decode('utf-8')
207
except UnicodeDecodeError:
208
s = s.decode('iso-8859-15')
216
212
def fixed_width(s):
218
214
expand tabs and turn spaces into "non-breaking spaces", so browsers won't
240
236
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
253
240
s = base64.encodestring(s).replace('\n', '')
254
241
while (len(s) > 0) and (s[-1] == '='):
329
316
return navigation.revid_list[navigation.position + offset]
318
navigation.last_in_page_revid = get_offset(navigation.pagesize - 1)
331
319
navigation.prev_page_revid = get_offset(-1 * navigation.pagesize)
332
320
navigation.next_page_revid = get_offset(1 * navigation.pagesize)
321
prev_page_revno = navigation.history.get_revno(
322
navigation.prev_page_revid)
323
next_page_revno = navigation.history.get_revno(
324
navigation.next_page_revid)
325
start_revno = navigation.history.get_revno(navigation.start_revid)
334
params = { 'file_id': navigation.file_id }
327
params = { 'filter_file_id': navigation.filter_file_id }
335
328
if getattr(navigation, 'query', None) is not None:
336
329
params['q'] = navigation.query
338
params['start_revid'] = navigation.start_revid
331
if getattr(navigation, 'start_revid', None) is not None:
332
params['start_revid'] = start_revno
340
334
if navigation.prev_page_revid:
341
navigation.prev_page_url = navigation.branch.url([ navigation.scan_url, navigation.prev_page_revid ], **get_context(**params))
335
navigation.prev_page_url = navigation.branch.context_url(
336
[navigation.scan_url, prev_page_revno], **params)
342
337
if navigation.next_page_revid:
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'):
338
navigation.next_page_url = navigation.branch.context_url(
339
[navigation.scan_url, next_page_revno], **params)
351
342
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)))
398
373
def _f(*a, **kw):
399
374
from loggerhead.lsprof import profile
419
394
# current location along the navigation path (while browsing)
420
395
# - starting revid (start_revid)
421
396
# the current beginning of navigation (navigation continues back to
422
# the original revision) -- this may not be along the primary revision
423
# path since the user may have navigated into a branch
397
# the original revision) -- this defines an 'alternate mainline'
398
# when the user navigates into a branch.
400
# the file being looked at
425
402
# if navigating the revisions that touched a file
427
404
# if navigating the revisions that matched a search query
438
415
# for re-ordering an existing page by different sort
440
417
t_context = threading.local()
441
_valid = ('start_revid', 'file_id', 'q', 'remember', 'compare_revid', 'sort')
418
_valid = ('start_revid', 'file_id', 'filter_file_id', 'q', 'remember',
419
'compare_revid', 'sort')
444
422
def set_context(map):