##// END OF EJS Templates
Fixed whoosh daemon, for depracated walk method
marcink -
r947:99850ac8 beta
parent child Browse files
Show More
@@ -1,525 +1,526 b''
1 """Helper functions
1 """Helper functions
2
2
3 Consists of functions to typically be used within templates, but also
3 Consists of functions to typically be used within templates, but also
4 available to Controllers. This module is available to both as 'h'.
4 available to Controllers. This module is available to both as 'h'.
5 """
5 """
6 import random
6 import random
7 import hashlib
7 import hashlib
8 from pygments.formatters import HtmlFormatter
8 from pygments.formatters import HtmlFormatter
9 from pygments import highlight as code_highlight
9 from pygments import highlight as code_highlight
10 from pylons import url, app_globals as g
10 from pylons import url, app_globals as g
11 from pylons.i18n.translation import _, ungettext
11 from pylons.i18n.translation import _, ungettext
12 from vcs.utils.annotate import annotate_highlight
12 from vcs.utils.annotate import annotate_highlight
13 from webhelpers.html import literal, HTML, escape
13 from webhelpers.html import literal, HTML, escape
14 from webhelpers.html.tools import *
14 from webhelpers.html.tools import *
15 from webhelpers.html.builder import make_tag
15 from webhelpers.html.builder import make_tag
16 from webhelpers.html.tags import auto_discovery_link, checkbox, css_classes, \
16 from webhelpers.html.tags import auto_discovery_link, checkbox, css_classes, \
17 end_form, file, form, hidden, image, javascript_link, link_to, link_to_if, \
17 end_form, file, form, hidden, image, javascript_link, link_to, link_to_if, \
18 link_to_unless, ol, required_legend, select, stylesheet_link, submit, text, \
18 link_to_unless, ol, required_legend, select, stylesheet_link, submit, text, \
19 password, textarea, title, ul, xml_declaration, radio
19 password, textarea, title, ul, xml_declaration, radio
20 from webhelpers.html.tools import auto_link, button_to, highlight, js_obfuscate, \
20 from webhelpers.html.tools import auto_link, button_to, highlight, js_obfuscate, \
21 mail_to, strip_links, strip_tags, tag_re
21 mail_to, strip_links, strip_tags, tag_re
22 from webhelpers.number import format_byte_size, format_bit_size
22 from webhelpers.number import format_byte_size, format_bit_size
23 from webhelpers.pylonslib import Flash as _Flash
23 from webhelpers.pylonslib import Flash as _Flash
24 from webhelpers.pylonslib.secure_form import secure_form
24 from webhelpers.pylonslib.secure_form import secure_form
25 from webhelpers.text import chop_at, collapse, convert_accented_entities, \
25 from webhelpers.text import chop_at, collapse, convert_accented_entities, \
26 convert_misc_entities, lchop, plural, rchop, remove_formatting, \
26 convert_misc_entities, lchop, plural, rchop, remove_formatting, \
27 replace_whitespace, urlify, truncate, wrap_paragraphs
27 replace_whitespace, urlify, truncate, wrap_paragraphs
28 from webhelpers.date import time_ago_in_words
28 from webhelpers.date import time_ago_in_words
29
29
30 from webhelpers.html.tags import _set_input_attrs, _set_id_attr, \
30 from webhelpers.html.tags import _set_input_attrs, _set_id_attr, \
31 convert_boolean_attrs, NotGiven
31 convert_boolean_attrs, NotGiven
32
32
33 def _reset(name, value=None, id=NotGiven, type="reset", **attrs):
33 def _reset(name, value=None, id=NotGiven, type="reset", **attrs):
34 """Reset button
34 """Reset button
35 """
35 """
36 _set_input_attrs(attrs, type, name, value)
36 _set_input_attrs(attrs, type, name, value)
37 _set_id_attr(attrs, id, name)
37 _set_id_attr(attrs, id, name)
38 convert_boolean_attrs(attrs, ["disabled"])
38 convert_boolean_attrs(attrs, ["disabled"])
39 return HTML.input(**attrs)
39 return HTML.input(**attrs)
40
40
41 reset = _reset
41 reset = _reset
42
42
43
43
44 def get_token():
44 def get_token():
45 """Return the current authentication token, creating one if one doesn't
45 """Return the current authentication token, creating one if one doesn't
46 already exist.
46 already exist.
47 """
47 """
48 token_key = "_authentication_token"
48 token_key = "_authentication_token"
49 from pylons import session
49 from pylons import session
50 if not token_key in session:
50 if not token_key in session:
51 try:
51 try:
52 token = hashlib.sha1(str(random.getrandbits(128))).hexdigest()
52 token = hashlib.sha1(str(random.getrandbits(128))).hexdigest()
53 except AttributeError: # Python < 2.4
53 except AttributeError: # Python < 2.4
54 token = hashlib.sha1(str(random.randrange(2 ** 128))).hexdigest()
54 token = hashlib.sha1(str(random.randrange(2 ** 128))).hexdigest()
55 session[token_key] = token
55 session[token_key] = token
56 if hasattr(session, 'save'):
56 if hasattr(session, 'save'):
57 session.save()
57 session.save()
58 return session[token_key]
58 return session[token_key]
59
59
60 class _GetError(object):
60 class _GetError(object):
61 """Get error from form_errors, and represent it as span wrapped error
61 """Get error from form_errors, and represent it as span wrapped error
62 message
62 message
63
63
64 :param field_name: field to fetch errors for
64 :param field_name: field to fetch errors for
65 :param form_errors: form errors dict
65 :param form_errors: form errors dict
66 """
66 """
67
67
68 def __call__(self, field_name, form_errors):
68 def __call__(self, field_name, form_errors):
69 tmpl = """<span class="error_msg">%s</span>"""
69 tmpl = """<span class="error_msg">%s</span>"""
70 if form_errors and form_errors.has_key(field_name):
70 if form_errors and form_errors.has_key(field_name):
71 return literal(tmpl % form_errors.get(field_name))
71 return literal(tmpl % form_errors.get(field_name))
72
72
73 get_error = _GetError()
73 get_error = _GetError()
74
74
75 def recursive_replace(str, replace=' '):
75 def recursive_replace(str, replace=' '):
76 """Recursive replace of given sign to just one instance
76 """Recursive replace of given sign to just one instance
77
77
78 :param str: given string
78 :param str: given string
79 :param replace: char to find and replace multiple instances
79 :param replace: char to find and replace multiple instances
80
80
81 Examples::
81 Examples::
82 >>> recursive_replace("Mighty---Mighty-Bo--sstones",'-')
82 >>> recursive_replace("Mighty---Mighty-Bo--sstones",'-')
83 'Mighty-Mighty-Bo-sstones'
83 'Mighty-Mighty-Bo-sstones'
84 """
84 """
85
85
86 if str.find(replace * 2) == -1:
86 if str.find(replace * 2) == -1:
87 return str
87 return str
88 else:
88 else:
89 str = str.replace(replace * 2, replace)
89 str = str.replace(replace * 2, replace)
90 return recursive_replace(str, replace)
90 return recursive_replace(str, replace)
91
91
92 class _ToolTip(object):
92 class _ToolTip(object):
93
93
94 def __call__(self, tooltip_title, trim_at=50):
94 def __call__(self, tooltip_title, trim_at=50):
95 """Special function just to wrap our text into nice formatted
95 """Special function just to wrap our text into nice formatted
96 autowrapped text
96 autowrapped text
97
97
98 :param tooltip_title:
98 :param tooltip_title:
99 """
99 """
100
100
101 return wrap_paragraphs(escape(tooltip_title), trim_at)\
101 return wrap_paragraphs(escape(tooltip_title), trim_at)\
102 .replace('\n', '<br/>')
102 .replace('\n', '<br/>')
103
103
104 def activate(self):
104 def activate(self):
105 """Adds tooltip mechanism to the given Html all tooltips have to have
105 """Adds tooltip mechanism to the given Html all tooltips have to have
106 set class `tooltip` and set attribute `tooltip_title`.
106 set class `tooltip` and set attribute `tooltip_title`.
107 Then a tooltip will be generated based on that. All with yui js tooltip
107 Then a tooltip will be generated based on that. All with yui js tooltip
108 """
108 """
109
109
110 js = '''
110 js = '''
111 YAHOO.util.Event.onDOMReady(function(){
111 YAHOO.util.Event.onDOMReady(function(){
112 function toolTipsId(){
112 function toolTipsId(){
113 var ids = [];
113 var ids = [];
114 var tts = YAHOO.util.Dom.getElementsByClassName('tooltip');
114 var tts = YAHOO.util.Dom.getElementsByClassName('tooltip');
115
115
116 for (var i = 0; i < tts.length; i++) {
116 for (var i = 0; i < tts.length; i++) {
117 //if element doesn't not have and id autogenerate one for tooltip
117 //if element doesn't not have and id autogenerate one for tooltip
118
118
119 if (!tts[i].id){
119 if (!tts[i].id){
120 tts[i].id='tt'+i*100;
120 tts[i].id='tt'+i*100;
121 }
121 }
122 ids.push(tts[i].id);
122 ids.push(tts[i].id);
123 }
123 }
124 return ids
124 return ids
125 };
125 };
126 var myToolTips = new YAHOO.widget.Tooltip("tooltip", {
126 var myToolTips = new YAHOO.widget.Tooltip("tooltip", {
127 context: toolTipsId(),
127 context: toolTipsId(),
128 monitorresize:false,
128 monitorresize:false,
129 xyoffset :[0,0],
129 xyoffset :[0,0],
130 autodismissdelay:300000,
130 autodismissdelay:300000,
131 hidedelay:5,
131 hidedelay:5,
132 showdelay:20,
132 showdelay:20,
133 });
133 });
134
134
135 // Set the text for the tooltip just before we display it. Lazy method
135 // Set the text for the tooltip just before we display it. Lazy method
136 myToolTips.contextTriggerEvent.subscribe(
136 myToolTips.contextTriggerEvent.subscribe(
137 function(type, args) {
137 function(type, args) {
138
138
139 var context = args[0];
139 var context = args[0];
140
140
141 //positioning of tooltip
141 //positioning of tooltip
142 var tt_w = this.element.clientWidth;//tooltip width
142 var tt_w = this.element.clientWidth;//tooltip width
143 var tt_h = this.element.clientHeight;//tooltip height
143 var tt_h = this.element.clientHeight;//tooltip height
144
144
145 var context_w = context.offsetWidth;
145 var context_w = context.offsetWidth;
146 var context_h = context.offsetHeight;
146 var context_h = context.offsetHeight;
147
147
148 var pos_x = YAHOO.util.Dom.getX(context);
148 var pos_x = YAHOO.util.Dom.getX(context);
149 var pos_y = YAHOO.util.Dom.getY(context);
149 var pos_y = YAHOO.util.Dom.getY(context);
150
150
151 var display_strategy = 'right';
151 var display_strategy = 'right';
152 var xy_pos = [0,0];
152 var xy_pos = [0,0];
153 switch (display_strategy){
153 switch (display_strategy){
154
154
155 case 'top':
155 case 'top':
156 var cur_x = (pos_x+context_w/2)-(tt_w/2);
156 var cur_x = (pos_x+context_w/2)-(tt_w/2);
157 var cur_y = (pos_y-tt_h-4);
157 var cur_y = (pos_y-tt_h-4);
158 xy_pos = [cur_x,cur_y];
158 xy_pos = [cur_x,cur_y];
159 break;
159 break;
160 case 'bottom':
160 case 'bottom':
161 var cur_x = (pos_x+context_w/2)-(tt_w/2);
161 var cur_x = (pos_x+context_w/2)-(tt_w/2);
162 var cur_y = pos_y+context_h+4;
162 var cur_y = pos_y+context_h+4;
163 xy_pos = [cur_x,cur_y];
163 xy_pos = [cur_x,cur_y];
164 break;
164 break;
165 case 'left':
165 case 'left':
166 var cur_x = (pos_x-tt_w-4);
166 var cur_x = (pos_x-tt_w-4);
167 var cur_y = pos_y-((tt_h/2)-context_h/2);
167 var cur_y = pos_y-((tt_h/2)-context_h/2);
168 xy_pos = [cur_x,cur_y];
168 xy_pos = [cur_x,cur_y];
169 break;
169 break;
170 case 'right':
170 case 'right':
171 var cur_x = (pos_x+context_w+4);
171 var cur_x = (pos_x+context_w+4);
172 var cur_y = pos_y-((tt_h/2)-context_h/2);
172 var cur_y = pos_y-((tt_h/2)-context_h/2);
173 xy_pos = [cur_x,cur_y];
173 xy_pos = [cur_x,cur_y];
174 break;
174 break;
175 default:
175 default:
176 var cur_x = (pos_x+context_w/2)-(tt_w/2);
176 var cur_x = (pos_x+context_w/2)-(tt_w/2);
177 var cur_y = pos_y-tt_h-4;
177 var cur_y = pos_y-tt_h-4;
178 xy_pos = [cur_x,cur_y];
178 xy_pos = [cur_x,cur_y];
179 break;
179 break;
180
180
181 }
181 }
182
182
183 this.cfg.setProperty("xy",xy_pos);
183 this.cfg.setProperty("xy",xy_pos);
184
184
185 });
185 });
186
186
187 //Mouse out
187 //Mouse out
188 myToolTips.contextMouseOutEvent.subscribe(
188 myToolTips.contextMouseOutEvent.subscribe(
189 function(type, args) {
189 function(type, args) {
190 var context = args[0];
190 var context = args[0];
191
191
192 });
192 });
193 });
193 });
194 '''
194 '''
195 return literal(js)
195 return literal(js)
196
196
197 tooltip = _ToolTip()
197 tooltip = _ToolTip()
198
198
199 class _FilesBreadCrumbs(object):
199 class _FilesBreadCrumbs(object):
200
200
201 def __call__(self, repo_name, rev, paths):
201 def __call__(self, repo_name, rev, paths):
202 url_l = [link_to(repo_name, url('files_home',
202 url_l = [link_to(repo_name, url('files_home',
203 repo_name=repo_name,
203 repo_name=repo_name,
204 revision=rev, f_path=''))]
204 revision=rev, f_path=''))]
205 paths_l = paths.split('/')
205 paths_l = paths.split('/')
206
206
207 for cnt, p in enumerate(paths_l):
207 for cnt, p in enumerate(paths_l):
208 if p != '':
208 if p != '':
209 url_l.append(link_to(p, url('files_home',
209 url_l.append(link_to(p, url('files_home',
210 repo_name=repo_name,
210 repo_name=repo_name,
211 revision=rev,
211 revision=rev,
212 f_path='/'.join(paths_l[:cnt + 1]))))
212 f_path='/'.join(paths_l[:cnt + 1]))))
213
213
214 return literal('/'.join(url_l))
214 return literal('/'.join(url_l))
215
215
216 files_breadcrumbs = _FilesBreadCrumbs()
216 files_breadcrumbs = _FilesBreadCrumbs()
217
217
218 class CodeHtmlFormatter(HtmlFormatter):
218 class CodeHtmlFormatter(HtmlFormatter):
219
219
220 def wrap(self, source, outfile):
220 def wrap(self, source, outfile):
221 return self._wrap_div(self._wrap_pre(self._wrap_code(source)))
221 return self._wrap_div(self._wrap_pre(self._wrap_code(source)))
222
222
223 def _wrap_code(self, source):
223 def _wrap_code(self, source):
224 for cnt, it in enumerate(source):
224 for cnt, it in enumerate(source):
225 i, t = it
225 i, t = it
226 t = '<div id="#S-%s">%s</div>' % (cnt + 1, t)
226 t = '<div id="#S-%s">%s</div>' % (cnt + 1, t)
227 yield i, t
227 yield i, t
228 def pygmentize(filenode, **kwargs):
228 def pygmentize(filenode, **kwargs):
229 """pygmentize function using pygments
229 """pygmentize function using pygments
230
230
231 :param filenode:
231 :param filenode:
232 """
232 """
233
233
234 return literal(code_highlight(filenode.content,
234 return literal(code_highlight(filenode.content,
235 filenode.lexer, CodeHtmlFormatter(**kwargs)))
235 filenode.lexer, CodeHtmlFormatter(**kwargs)))
236
236
237 def pygmentize_annotation(filenode, **kwargs):
237 def pygmentize_annotation(filenode, **kwargs):
238 """pygmentize function for annotation
238 """pygmentize function for annotation
239
239
240 :param filenode:
240 :param filenode:
241 """
241 """
242
242
243 color_dict = {}
243 color_dict = {}
244 def gen_color():
244 def gen_color(n=10000):
245 """generator for getting 10k of evenly distibuted colors using hsv color
245 """generator for getting n of evenly distributed colors using
246 and golden ratio.
246 hsv color and golden ratio. It always return same order of colors
247
248 :returns: RGB tuple
247 """
249 """
248 import colorsys
250 import colorsys
249 n = 10000
250 golden_ratio = 0.618033988749895
251 golden_ratio = 0.618033988749895
251 h = 0.22717784590367374
252 h = 0.22717784590367374
252 #generate 10k nice web friendly colors in the same order
253
253 for c in xrange(n):
254 for c in xrange(n):
254 h += golden_ratio
255 h += golden_ratio
255 h %= 1
256 h %= 1
256 HSV_tuple = [h, 0.95, 0.95]
257 HSV_tuple = [h, 0.95, 0.95]
257 RGB_tuple = colorsys.hsv_to_rgb(*HSV_tuple)
258 RGB_tuple = colorsys.hsv_to_rgb(*HSV_tuple)
258 yield map(lambda x:str(int(x * 256)), RGB_tuple)
259 yield map(lambda x:str(int(x * 256)), RGB_tuple)
259
260
260 cgenerator = gen_color()
261 cgenerator = gen_color()
261
262
262 def get_color_string(cs):
263 def get_color_string(cs):
263 if color_dict.has_key(cs):
264 if color_dict.has_key(cs):
264 col = color_dict[cs]
265 col = color_dict[cs]
265 else:
266 else:
266 col = color_dict[cs] = cgenerator.next()
267 col = color_dict[cs] = cgenerator.next()
267 return "color: rgb(%s)! important;" % (', '.join(col))
268 return "color: rgb(%s)! important;" % (', '.join(col))
268
269
269 def url_func(changeset):
270 def url_func(changeset):
270 tooltip_html = "<div style='font-size:0.8em'><b>Author:</b>" + \
271 tooltip_html = "<div style='font-size:0.8em'><b>Author:</b>" + \
271 " %s<br/><b>Date:</b> %s</b><br/><b>Message:</b> %s<br/></div>"
272 " %s<br/><b>Date:</b> %s</b><br/><b>Message:</b> %s<br/></div>"
272
273
273 tooltip_html = tooltip_html % (changeset.author,
274 tooltip_html = tooltip_html % (changeset.author,
274 changeset.date,
275 changeset.date,
275 tooltip(changeset.message))
276 tooltip(changeset.message))
276 lnk_format = '%5s:%s' % ('r%s' % changeset.revision,
277 lnk_format = '%5s:%s' % ('r%s' % changeset.revision,
277 short_id(changeset.raw_id))
278 short_id(changeset.raw_id))
278 uri = link_to(
279 uri = link_to(
279 lnk_format,
280 lnk_format,
280 url('changeset_home', repo_name=changeset.repository.name,
281 url('changeset_home', repo_name=changeset.repository.name,
281 revision=changeset.raw_id),
282 revision=changeset.raw_id),
282 style=get_color_string(changeset.raw_id),
283 style=get_color_string(changeset.raw_id),
283 class_='tooltip',
284 class_='tooltip',
284 title=tooltip_html
285 title=tooltip_html
285 )
286 )
286
287
287 uri += '\n'
288 uri += '\n'
288 return uri
289 return uri
289 return literal(annotate_highlight(filenode, url_func, **kwargs))
290 return literal(annotate_highlight(filenode, url_func, **kwargs))
290
291
291 def repo_name_slug(value):
292 def repo_name_slug(value):
292 """Return slug of name of repository
293 """Return slug of name of repository
293 This function is called on each creation/modification
294 This function is called on each creation/modification
294 of repository to prevent bad names in repo
295 of repository to prevent bad names in repo
295 """
296 """
296
297
297 slug = remove_formatting(value)
298 slug = remove_formatting(value)
298 slug = strip_tags(slug)
299 slug = strip_tags(slug)
299
300
300 for c in """=[]\;'"<>,/~!@#$%^&*()+{}|: """:
301 for c in """=[]\;'"<>,/~!@#$%^&*()+{}|: """:
301 slug = slug.replace(c, '-')
302 slug = slug.replace(c, '-')
302 slug = recursive_replace(slug, '-')
303 slug = recursive_replace(slug, '-')
303 slug = collapse(slug, '-')
304 slug = collapse(slug, '-')
304 return slug
305 return slug
305
306
306 def get_changeset_safe(repo, rev):
307 def get_changeset_safe(repo, rev):
307 from vcs.backends.base import BaseRepository
308 from vcs.backends.base import BaseRepository
308 from vcs.exceptions import RepositoryError
309 from vcs.exceptions import RepositoryError
309 if not isinstance(repo, BaseRepository):
310 if not isinstance(repo, BaseRepository):
310 raise Exception('You must pass an Repository '
311 raise Exception('You must pass an Repository '
311 'object as first argument got %s', type(repo))
312 'object as first argument got %s', type(repo))
312
313
313 try:
314 try:
314 cs = repo.get_changeset(rev)
315 cs = repo.get_changeset(rev)
315 except RepositoryError:
316 except RepositoryError:
316 from rhodecode.lib.utils import EmptyChangeset
317 from rhodecode.lib.utils import EmptyChangeset
317 cs = EmptyChangeset()
318 cs = EmptyChangeset()
318 return cs
319 return cs
319
320
320
321
321 flash = _Flash()
322 flash = _Flash()
322
323
323
324
324 #==============================================================================
325 #==============================================================================
325 # MERCURIAL FILTERS available via h.
326 # MERCURIAL FILTERS available via h.
326 #==============================================================================
327 #==============================================================================
327 from mercurial import util
328 from mercurial import util
328 from mercurial.templatefilters import person as _person
329 from mercurial.templatefilters import person as _person
329
330
330 def _age(curdate):
331 def _age(curdate):
331 """turns a datetime into an age string."""
332 """turns a datetime into an age string."""
332
333
333 if not curdate:
334 if not curdate:
334 return ''
335 return ''
335
336
336 from datetime import timedelta, datetime
337 from datetime import timedelta, datetime
337
338
338 agescales = [("year", 3600 * 24 * 365),
339 agescales = [("year", 3600 * 24 * 365),
339 ("month", 3600 * 24 * 30),
340 ("month", 3600 * 24 * 30),
340 ("day", 3600 * 24),
341 ("day", 3600 * 24),
341 ("hour", 3600),
342 ("hour", 3600),
342 ("minute", 60),
343 ("minute", 60),
343 ("second", 1), ]
344 ("second", 1), ]
344
345
345 age = datetime.now() - curdate
346 age = datetime.now() - curdate
346 age_seconds = (age.days * agescales[2][1]) + age.seconds
347 age_seconds = (age.days * agescales[2][1]) + age.seconds
347 pos = 1
348 pos = 1
348 for scale in agescales:
349 for scale in agescales:
349 if scale[1] <= age_seconds:
350 if scale[1] <= age_seconds:
350 if pos == 6:pos = 5
351 if pos == 6:pos = 5
351 return time_ago_in_words(curdate, agescales[pos][0]) + ' ' + _('ago')
352 return time_ago_in_words(curdate, agescales[pos][0]) + ' ' + _('ago')
352 pos += 1
353 pos += 1
353
354
354 return _('just now')
355 return _('just now')
355
356
356 age = lambda x:_age(x)
357 age = lambda x:_age(x)
357 capitalize = lambda x: x.capitalize()
358 capitalize = lambda x: x.capitalize()
358 email = util.email
359 email = util.email
359 email_or_none = lambda x: util.email(x) if util.email(x) != x else None
360 email_or_none = lambda x: util.email(x) if util.email(x) != x else None
360 person = lambda x: _person(x)
361 person = lambda x: _person(x)
361 short_id = lambda x: x[:12]
362 short_id = lambda x: x[:12]
362
363
363
364
364 def bool2icon(value):
365 def bool2icon(value):
365 """Returns True/False values represented as small html image of true/false
366 """Returns True/False values represented as small html image of true/false
366 icons
367 icons
367
368
368 :param value: bool value
369 :param value: bool value
369 """
370 """
370
371
371 if value is True:
372 if value is True:
372 return HTML.tag('img', src="/images/icons/accept.png", alt=_('True'))
373 return HTML.tag('img', src="/images/icons/accept.png", alt=_('True'))
373
374
374 if value is False:
375 if value is False:
375 return HTML.tag('img', src="/images/icons/cancel.png", alt=_('False'))
376 return HTML.tag('img', src="/images/icons/cancel.png", alt=_('False'))
376
377
377 return value
378 return value
378
379
379
380
380 def action_parser(user_log):
381 def action_parser(user_log):
381 """This helper will map the specified string action into translated
382 """This helper will map the specified string action into translated
382 fancy names with icons and links
383 fancy names with icons and links
383
384
384 :param user_log: user log instance
385 :param user_log: user log instance
385 """
386 """
386
387
387 action = user_log.action
388 action = user_log.action
388 action_params = ' '
389 action_params = ' '
389
390
390 x = action.split(':')
391 x = action.split(':')
391
392
392 if len(x) > 1:
393 if len(x) > 1:
393 action, action_params = x
394 action, action_params = x
394
395
395 def get_cs_links():
396 def get_cs_links():
396 if action == 'push':
397 if action == 'push':
397 revs_limit = 5 #display this amount always
398 revs_limit = 5 #display this amount always
398 revs_top_limit = 50 #show upto this amount of changesets hidden
399 revs_top_limit = 50 #show upto this amount of changesets hidden
399 revs = action_params.split(',')
400 revs = action_params.split(',')
400 repo_name = user_log.repository.repo_name
401 repo_name = user_log.repository.repo_name
401 from rhodecode.model.scm import ScmModel
402 from rhodecode.model.scm import ScmModel
402
403
403 message = lambda rev: get_changeset_safe(ScmModel().get(repo_name),
404 message = lambda rev: get_changeset_safe(ScmModel().get(repo_name),
404 rev).message
405 rev).message
405
406
406 cs_links = " " + ', '.join ([link_to(rev,
407 cs_links = " " + ', '.join ([link_to(rev,
407 url('changeset_home',
408 url('changeset_home',
408 repo_name=repo_name,
409 repo_name=repo_name,
409 revision=rev), title=tooltip(message(rev)),
410 revision=rev), title=tooltip(message(rev)),
410 class_='tooltip') for rev in revs[:revs_limit] ])
411 class_='tooltip') for rev in revs[:revs_limit] ])
411 if len(revs) > revs_limit:
412 if len(revs) > revs_limit:
412 uniq_id = revs[0]
413 uniq_id = revs[0]
413 html_tmpl = ('<span> %s '
414 html_tmpl = ('<span> %s '
414 '<a class="show_more" id="_%s" href="#">%s</a> '
415 '<a class="show_more" id="_%s" href="#">%s</a> '
415 '%s</span>')
416 '%s</span>')
416 cs_links += html_tmpl % (_('and'), uniq_id, _('%s more') \
417 cs_links += html_tmpl % (_('and'), uniq_id, _('%s more') \
417 % (len(revs) - revs_limit),
418 % (len(revs) - revs_limit),
418 _('revisions'))
419 _('revisions'))
419
420
420 html_tmpl = '<span id="%s" style="display:none"> %s </span>'
421 html_tmpl = '<span id="%s" style="display:none"> %s </span>'
421 cs_links += html_tmpl % (uniq_id, ', '.join([link_to(rev,
422 cs_links += html_tmpl % (uniq_id, ', '.join([link_to(rev,
422 url('changeset_home',
423 url('changeset_home',
423 repo_name=repo_name, revision=rev),
424 repo_name=repo_name, revision=rev),
424 title=message(rev), class_='tooltip')
425 title=message(rev), class_='tooltip')
425 for rev in revs[revs_limit:revs_top_limit]]))
426 for rev in revs[revs_limit:revs_top_limit]]))
426
427
427 return cs_links
428 return cs_links
428 return ''
429 return ''
429
430
430 def get_fork_name():
431 def get_fork_name():
431 if action == 'user_forked_repo':
432 if action == 'user_forked_repo':
432 from rhodecode.model.scm import ScmModel
433 from rhodecode.model.scm import ScmModel
433 repo_name = action_params
434 repo_name = action_params
434 repo = ScmModel().get(repo_name)
435 repo = ScmModel().get(repo_name)
435 if repo is None:
436 if repo is None:
436 return repo_name
437 return repo_name
437 return link_to(action_params, url('summary_home',
438 return link_to(action_params, url('summary_home',
438 repo_name=repo.name,),
439 repo_name=repo.name,),
439 title=repo.dbrepo.description)
440 title=repo.dbrepo.description)
440 return ''
441 return ''
441 map = {'user_deleted_repo':_('User [deleted] repository'),
442 map = {'user_deleted_repo':_('User [deleted] repository'),
442 'user_created_repo':_('User [created] repository'),
443 'user_created_repo':_('User [created] repository'),
443 'user_forked_repo':_('User [forked] repository as: %s') % get_fork_name(),
444 'user_forked_repo':_('User [forked] repository as: %s') % get_fork_name(),
444 'user_updated_repo':_('User [updated] repository'),
445 'user_updated_repo':_('User [updated] repository'),
445 'admin_deleted_repo':_('Admin [delete] repository'),
446 'admin_deleted_repo':_('Admin [delete] repository'),
446 'admin_created_repo':_('Admin [created] repository'),
447 'admin_created_repo':_('Admin [created] repository'),
447 'admin_forked_repo':_('Admin [forked] repository'),
448 'admin_forked_repo':_('Admin [forked] repository'),
448 'admin_updated_repo':_('Admin [updated] repository'),
449 'admin_updated_repo':_('Admin [updated] repository'),
449 'push':_('[Pushed] %s') % get_cs_links(),
450 'push':_('[Pushed] %s') % get_cs_links(),
450 'pull':_('[Pulled]'),
451 'pull':_('[Pulled]'),
451 'started_following_repo':_('User [started following] repository'),
452 'started_following_repo':_('User [started following] repository'),
452 'stopped_following_repo':_('User [stopped following] repository'),
453 'stopped_following_repo':_('User [stopped following] repository'),
453 }
454 }
454
455
455 action_str = map.get(action, action)
456 action_str = map.get(action, action)
456 return literal(action_str.replace('[', '<span class="journal_highlight">')\
457 return literal(action_str.replace('[', '<span class="journal_highlight">')\
457 .replace(']', '</span>'))
458 .replace(']', '</span>'))
458
459
459 def action_parser_icon(user_log):
460 def action_parser_icon(user_log):
460 action = user_log.action
461 action = user_log.action
461 action_params = None
462 action_params = None
462 x = action.split(':')
463 x = action.split(':')
463
464
464 if len(x) > 1:
465 if len(x) > 1:
465 action, action_params = x
466 action, action_params = x
466
467
467 tmpl = """<img src="/images/icons/%s" alt="%s"/>"""
468 tmpl = """<img src="/images/icons/%s" alt="%s"/>"""
468 map = {'user_deleted_repo':'database_delete.png',
469 map = {'user_deleted_repo':'database_delete.png',
469 'user_created_repo':'database_add.png',
470 'user_created_repo':'database_add.png',
470 'user_forked_repo':'arrow_divide.png',
471 'user_forked_repo':'arrow_divide.png',
471 'user_updated_repo':'database_edit.png',
472 'user_updated_repo':'database_edit.png',
472 'admin_deleted_repo':'database_delete.png',
473 'admin_deleted_repo':'database_delete.png',
473 'admin_created_repo':'database_add.png',
474 'admin_created_repo':'database_add.png',
474 'admin_forked_repo':'arrow_divide.png',
475 'admin_forked_repo':'arrow_divide.png',
475 'admin_updated_repo':'database_edit.png',
476 'admin_updated_repo':'database_edit.png',
476 'push':'script_add.png',
477 'push':'script_add.png',
477 'pull':'down_16.png',
478 'pull':'down_16.png',
478 'started_following_repo':'heart_add.png',
479 'started_following_repo':'heart_add.png',
479 'stopped_following_repo':'heart_delete.png',
480 'stopped_following_repo':'heart_delete.png',
480 }
481 }
481 return literal(tmpl % (map.get(action, action), action))
482 return literal(tmpl % (map.get(action, action), action))
482
483
483
484
484 #==============================================================================
485 #==============================================================================
485 # PERMS
486 # PERMS
486 #==============================================================================
487 #==============================================================================
487 from rhodecode.lib.auth import HasPermissionAny, HasPermissionAll, \
488 from rhodecode.lib.auth import HasPermissionAny, HasPermissionAll, \
488 HasRepoPermissionAny, HasRepoPermissionAll
489 HasRepoPermissionAny, HasRepoPermissionAll
489
490
490 #==============================================================================
491 #==============================================================================
491 # GRAVATAR URL
492 # GRAVATAR URL
492 #==============================================================================
493 #==============================================================================
493 import hashlib
494 import hashlib
494 import urllib
495 import urllib
495 from pylons import request
496 from pylons import request
496
497
497 def gravatar_url(email_address, size=30):
498 def gravatar_url(email_address, size=30):
498 ssl_enabled = 'https' == request.environ.get('wsgi.url_scheme')
499 ssl_enabled = 'https' == request.environ.get('wsgi.url_scheme')
499 default = 'identicon'
500 default = 'identicon'
500 baseurl_nossl = "http://www.gravatar.com/avatar/"
501 baseurl_nossl = "http://www.gravatar.com/avatar/"
501 baseurl_ssl = "https://secure.gravatar.com/avatar/"
502 baseurl_ssl = "https://secure.gravatar.com/avatar/"
502 baseurl = baseurl_ssl if ssl_enabled else baseurl_nossl
503 baseurl = baseurl_ssl if ssl_enabled else baseurl_nossl
503
504
504
505
505 # construct the url
506 # construct the url
506 gravatar_url = baseurl + hashlib.md5(email_address.lower()).hexdigest() + "?"
507 gravatar_url = baseurl + hashlib.md5(email_address.lower()).hexdigest() + "?"
507 gravatar_url += urllib.urlencode({'d':default, 's':str(size)})
508 gravatar_url += urllib.urlencode({'d':default, 's':str(size)})
508
509
509 return gravatar_url
510 return gravatar_url
510
511
511 def safe_unicode(str):
512 def safe_unicode(str):
512 """safe unicode function. In case of UnicodeDecode error we try to return
513 """safe unicode function. In case of UnicodeDecode error we try to return
513 unicode with errors replace, if this failes we return unicode with
514 unicode with errors replace, if this failes we return unicode with
514 string_escape decoding """
515 string_escape decoding """
515
516
516 try:
517 try:
517 u_str = unicode(str)
518 u_str = unicode(str)
518 except UnicodeDecodeError:
519 except UnicodeDecodeError:
519 try:
520 try:
520 u_str = unicode(str, 'utf-8', 'replace')
521 u_str = unicode(str, 'utf-8', 'replace')
521 except UnicodeDecodeError:
522 except UnicodeDecodeError:
522 #incase we have a decode error just represent as byte string
523 #incase we have a decode error just represent as byte string
523 u_str = unicode(str(str).encode('string_escape'))
524 u_str = unicode(str(str).encode('string_escape'))
524
525
525 return u_str
526 return u_str
@@ -1,236 +1,237 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.lib.indexers.daemon
3 rhodecode.lib.indexers.daemon
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 A deamon will read from task table and run tasks
6 A deamon will read from task table and run tasks
7
7
8 :created_on: Jan 26, 2010
8 :created_on: Jan 26, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
13 # This program is free software; you can redistribute it and/or
14 # modify it under the terms of the GNU General Public License
14 # modify it under the terms of the GNU General Public License
15 # as published by the Free Software Foundation; version 2
15 # as published by the Free Software Foundation; version 2
16 # of the License or (at your opinion) any later version of the license.
16 # of the License or (at your opinion) any later version of the license.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
26 # MA 02110-1301, USA.
27
27
28 import sys
28 import sys
29 import os
29 import os
30 import traceback
30 import traceback
31 from os.path import dirname as dn
31 from os.path import dirname as dn
32 from os.path import join as jn
32 from os.path import join as jn
33
33
34 #to get the rhodecode import
34 #to get the rhodecode import
35 project_path = dn(dn(dn(dn(os.path.realpath(__file__)))))
35 project_path = dn(dn(dn(dn(os.path.realpath(__file__)))))
36 sys.path.append(project_path)
36 sys.path.append(project_path)
37
37
38
38
39 from rhodecode.model.scm import ScmModel
39 from rhodecode.model.scm import ScmModel
40 from rhodecode.lib.helpers import safe_unicode
40 from rhodecode.lib.helpers import safe_unicode
41 from whoosh.index import create_in, open_dir
41 from whoosh.index import create_in, open_dir
42 from shutil import rmtree
42 from shutil import rmtree
43 from rhodecode.lib.indexers import INDEX_EXTENSIONS, SCHEMA, IDX_NAME
43 from rhodecode.lib.indexers import INDEX_EXTENSIONS, SCHEMA, IDX_NAME
44
44
45 from time import mktime
45 from time import mktime
46 from vcs.exceptions import ChangesetError, RepositoryError
46 from vcs.exceptions import ChangesetError, RepositoryError
47
47
48 import logging
48 import logging
49
49
50 log = logging.getLogger('whooshIndexer')
50 log = logging.getLogger('whooshIndexer')
51 # create logger
51 # create logger
52 log.setLevel(logging.DEBUG)
52 log.setLevel(logging.DEBUG)
53 log.propagate = False
53 log.propagate = False
54 # create console handler and set level to debug
54 # create console handler and set level to debug
55 ch = logging.StreamHandler()
55 ch = logging.StreamHandler()
56 ch.setLevel(logging.DEBUG)
56 ch.setLevel(logging.DEBUG)
57
57
58 # create formatter
58 # create formatter
59 formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
59 formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
60
60
61 # add formatter to ch
61 # add formatter to ch
62 ch.setFormatter(formatter)
62 ch.setFormatter(formatter)
63
63
64 # add ch to logger
64 # add ch to logger
65 log.addHandler(ch)
65 log.addHandler(ch)
66
66
67 class WhooshIndexingDaemon(object):
67 class WhooshIndexingDaemon(object):
68 """
68 """
69 Deamon for atomic jobs
69 Deamon for atomic jobs
70 """
70 """
71
71
72 def __init__(self, indexname='HG_INDEX', index_location=None,
72 def __init__(self, indexname='HG_INDEX', index_location=None,
73 repo_location=None, sa=None, repo_list=None):
73 repo_location=None, sa=None, repo_list=None):
74 self.indexname = indexname
74 self.indexname = indexname
75
75
76 self.index_location = index_location
76 self.index_location = index_location
77 if not index_location:
77 if not index_location:
78 raise Exception('You have to provide index location')
78 raise Exception('You have to provide index location')
79
79
80 self.repo_location = repo_location
80 self.repo_location = repo_location
81 if not repo_location:
81 if not repo_location:
82 raise Exception('You have to provide repositories location')
82 raise Exception('You have to provide repositories location')
83
83
84 self.repo_paths = ScmModel(sa).repo_scan(self.repo_location, None)
84 self.repo_paths = ScmModel(sa).repo_scan(self.repo_location, None)
85
85
86 if repo_list:
86 if repo_list:
87 filtered_repo_paths = {}
87 filtered_repo_paths = {}
88 for repo_name, repo in self.repo_paths.items():
88 for repo_name, repo in self.repo_paths.items():
89 if repo_name in repo_list:
89 if repo_name in repo_list:
90 filtered_repo_paths[repo.name] = repo
90 filtered_repo_paths[repo.name] = repo
91
91
92 self.repo_paths = filtered_repo_paths
92 self.repo_paths = filtered_repo_paths
93
93
94
94
95 self.initial = False
95 self.initial = False
96 if not os.path.isdir(self.index_location):
96 if not os.path.isdir(self.index_location):
97 os.makedirs(self.index_location)
97 os.makedirs(self.index_location)
98 log.info('Cannot run incremental index since it does not'
98 log.info('Cannot run incremental index since it does not'
99 ' yet exist running full build')
99 ' yet exist running full build')
100 self.initial = True
100 self.initial = True
101
101
102 def get_paths(self, repo):
102 def get_paths(self, repo):
103 """recursive walk in root dir and return a set of all path in that dir
103 """recursive walk in root dir and return a set of all path in that dir
104 based on repository walk function
104 based on repository walk function
105 """
105 """
106 index_paths_ = set()
106 index_paths_ = set()
107 try:
107 try:
108 for topnode, dirs, files in repo.walk('/', 'tip'):
108 tip = repo.get_changeset('tip')
109 for topnode, dirs, files in tip.walk('/'):
109 for f in files:
110 for f in files:
110 index_paths_.add(jn(repo.path, f.path))
111 index_paths_.add(jn(repo.path, f.path))
111 for dir in dirs:
112 for dir in dirs:
112 for f in files:
113 for f in files:
113 index_paths_.add(jn(repo.path, f.path))
114 index_paths_.add(jn(repo.path, f.path))
114
115
115 except RepositoryError, e:
116 except RepositoryError, e:
116 log.debug(traceback.format_exc())
117 log.debug(traceback.format_exc())
117 pass
118 pass
118 return index_paths_
119 return index_paths_
119
120
120 def get_node(self, repo, path):
121 def get_node(self, repo, path):
121 n_path = path[len(repo.path) + 1:]
122 n_path = path[len(repo.path) + 1:]
122 node = repo.get_changeset().get_node(n_path)
123 node = repo.get_changeset().get_node(n_path)
123 return node
124 return node
124
125
125 def get_node_mtime(self, node):
126 def get_node_mtime(self, node):
126 return mktime(node.last_changeset.date.timetuple())
127 return mktime(node.last_changeset.date.timetuple())
127
128
128 def add_doc(self, writer, path, repo):
129 def add_doc(self, writer, path, repo):
129 """Adding doc to writer this function itself fetches data from
130 """Adding doc to writer this function itself fetches data from
130 the instance of vcs backend"""
131 the instance of vcs backend"""
131 node = self.get_node(repo, path)
132 node = self.get_node(repo, path)
132
133
133 #we just index the content of chosen files, and skip binary files
134 #we just index the content of chosen files, and skip binary files
134 if node.extension in INDEX_EXTENSIONS and not node.is_binary:
135 if node.extension in INDEX_EXTENSIONS and not node.is_binary:
135
136
136 u_content = node.content
137 u_content = node.content
137 if not isinstance(u_content, unicode):
138 if not isinstance(u_content, unicode):
138 log.warning(' >> %s Could not get this content as unicode '
139 log.warning(' >> %s Could not get this content as unicode '
139 'replacing with empty content', path)
140 'replacing with empty content', path)
140 u_content = u''
141 u_content = u''
141 else:
142 else:
142 log.debug(' >> %s [WITH CONTENT]' % path)
143 log.debug(' >> %s [WITH CONTENT]' % path)
143
144
144 else:
145 else:
145 log.debug(' >> %s' % path)
146 log.debug(' >> %s' % path)
146 #just index file name without it's content
147 #just index file name without it's content
147 u_content = u''
148 u_content = u''
148
149
149 writer.add_document(owner=unicode(repo.contact),
150 writer.add_document(owner=unicode(repo.contact),
150 repository=safe_unicode(repo.name),
151 repository=safe_unicode(repo.name),
151 path=safe_unicode(path),
152 path=safe_unicode(path),
152 content=u_content,
153 content=u_content,
153 modtime=self.get_node_mtime(node),
154 modtime=self.get_node_mtime(node),
154 extension=node.extension)
155 extension=node.extension)
155
156
156
157
157 def build_index(self):
158 def build_index(self):
158 if os.path.exists(self.index_location):
159 if os.path.exists(self.index_location):
159 log.debug('removing previous index')
160 log.debug('removing previous index')
160 rmtree(self.index_location)
161 rmtree(self.index_location)
161
162
162 if not os.path.exists(self.index_location):
163 if not os.path.exists(self.index_location):
163 os.mkdir(self.index_location)
164 os.mkdir(self.index_location)
164
165
165 idx = create_in(self.index_location, SCHEMA, indexname=IDX_NAME)
166 idx = create_in(self.index_location, SCHEMA, indexname=IDX_NAME)
166 writer = idx.writer()
167 writer = idx.writer()
167
168
168 for repo in self.repo_paths.values():
169 for repo in self.repo_paths.values():
169 log.debug('building index @ %s' % repo.path)
170 log.debug('building index @ %s' % repo.path)
170
171
171 for idx_path in self.get_paths(repo):
172 for idx_path in self.get_paths(repo):
172 self.add_doc(writer, idx_path, repo)
173 self.add_doc(writer, idx_path, repo)
173
174
174 log.debug('>> COMMITING CHANGES <<')
175 log.debug('>> COMMITING CHANGES <<')
175 writer.commit(merge=True)
176 writer.commit(merge=True)
176 log.debug('>>> FINISHED BUILDING INDEX <<<')
177 log.debug('>>> FINISHED BUILDING INDEX <<<')
177
178
178
179
179 def update_index(self):
180 def update_index(self):
180 log.debug('STARTING INCREMENTAL INDEXING UPDATE')
181 log.debug('STARTING INCREMENTAL INDEXING UPDATE')
181
182
182 idx = open_dir(self.index_location, indexname=self.indexname)
183 idx = open_dir(self.index_location, indexname=self.indexname)
183 # The set of all paths in the index
184 # The set of all paths in the index
184 indexed_paths = set()
185 indexed_paths = set()
185 # The set of all paths we need to re-index
186 # The set of all paths we need to re-index
186 to_index = set()
187 to_index = set()
187
188
188 reader = idx.reader()
189 reader = idx.reader()
189 writer = idx.writer()
190 writer = idx.writer()
190
191
191 # Loop over the stored fields in the index
192 # Loop over the stored fields in the index
192 for fields in reader.all_stored_fields():
193 for fields in reader.all_stored_fields():
193 indexed_path = fields['path']
194 indexed_path = fields['path']
194 indexed_paths.add(indexed_path)
195 indexed_paths.add(indexed_path)
195
196
196 repo = self.repo_paths[fields['repository']]
197 repo = self.repo_paths[fields['repository']]
197
198
198 try:
199 try:
199 node = self.get_node(repo, indexed_path)
200 node = self.get_node(repo, indexed_path)
200 except ChangesetError:
201 except ChangesetError:
201 # This file was deleted since it was indexed
202 # This file was deleted since it was indexed
202 log.debug('removing from index %s' % indexed_path)
203 log.debug('removing from index %s' % indexed_path)
203 writer.delete_by_term('path', indexed_path)
204 writer.delete_by_term('path', indexed_path)
204
205
205 else:
206 else:
206 # Check if this file was changed since it was indexed
207 # Check if this file was changed since it was indexed
207 indexed_time = fields['modtime']
208 indexed_time = fields['modtime']
208 mtime = self.get_node_mtime(node)
209 mtime = self.get_node_mtime(node)
209 if mtime > indexed_time:
210 if mtime > indexed_time:
210 # The file has changed, delete it and add it to the list of
211 # The file has changed, delete it and add it to the list of
211 # files to reindex
212 # files to reindex
212 log.debug('adding to reindex list %s' % indexed_path)
213 log.debug('adding to reindex list %s' % indexed_path)
213 writer.delete_by_term('path', indexed_path)
214 writer.delete_by_term('path', indexed_path)
214 to_index.add(indexed_path)
215 to_index.add(indexed_path)
215
216
216 # Loop over the files in the filesystem
217 # Loop over the files in the filesystem
217 # Assume we have a function that gathers the filenames of the
218 # Assume we have a function that gathers the filenames of the
218 # documents to be indexed
219 # documents to be indexed
219 for repo in self.repo_paths.values():
220 for repo in self.repo_paths.values():
220 for path in self.get_paths(repo):
221 for path in self.get_paths(repo):
221 if path in to_index or path not in indexed_paths:
222 if path in to_index or path not in indexed_paths:
222 # This is either a file that's changed, or a new file
223 # This is either a file that's changed, or a new file
223 # that wasn't indexed before. So index it!
224 # that wasn't indexed before. So index it!
224 self.add_doc(writer, path, repo)
225 self.add_doc(writer, path, repo)
225 log.debug('re indexing %s' % path)
226 log.debug('re indexing %s' % path)
226
227
227 log.debug('>> COMMITING CHANGES <<')
228 log.debug('>> COMMITING CHANGES <<')
228 writer.commit(merge=True)
229 writer.commit(merge=True)
229 log.debug('>>> FINISHED REBUILDING INDEX <<<')
230 log.debug('>>> FINISHED REBUILDING INDEX <<<')
230
231
231 def run(self, full_index=False):
232 def run(self, full_index=False):
232 """Run daemon"""
233 """Run daemon"""
233 if full_index or self.initial:
234 if full_index or self.initial:
234 self.build_index()
235 self.build_index()
235 else:
236 else:
236 self.update_index()
237 self.update_index()
General Comments 0
You need to be logged in to leave comments. Login now