Improved the duplicate ticket search. Now it search for partial and complete phrases enter into the summary in new ticket. change the keywords suggest : add the query page javascript to separate file, change the db use so it can find keywords in query page also.
On Wed, Aug 6, 2014 at 11:32 PM, <[email protected]> wrote: > Author: thimal > Date: Wed Aug 6 18:02:52 2014 > New Revision: 1616294 > > URL: http://svn.apache.org/r1616294 > Log: > Improved the duplicate ticket search. Now it search for partial and > complete phrases enter into the summary in new ticket. > change the keywords suggest : add the query page javascript to separate > file, change the db use so it can find keywords in query page also. > > Modified: > > bloodhound/branches/bep_0013_dynamic_clientside_features/bloodhound_theme/bhtheme/theme.py > > Modified: > bloodhound/branches/bep_0013_dynamic_clientside_features/bloodhound_theme/bhtheme/theme.py > URL: > http://svn.apache.org/viewvc/bloodhound/branches/bep_0013_dynamic_clientside_features/bloodhound_theme/bhtheme/theme.py?rev=1616294&r1=1616293&r2=1616294&view=diff > > ============================================================================== > --- > bloodhound/branches/bep_0013_dynamic_clientside_features/bloodhound_theme/bhtheme/theme.py > (original) > +++ > bloodhound/branches/bep_0013_dynamic_clientside_features/bloodhound_theme/bhtheme/theme.py > Wed Aug 6 18:02:52 2014 > @@ -19,8 +19,9 @@ > > import sys > > +import re > + > from trac.util.datefmt import format_datetime, user_time > -from collections import Counter > > import fnmatch > > @@ -28,8 +29,6 @@ from genshi.builder import tag > from genshi.core import TEXT, Markup > from genshi.filters.transform import Transformer > from genshi.output import DocType > -from bhsearch.api import BloodhoundSearchApi > -from bhsearch.web_ui import RequestContext > > from trac.config import ListOption, Option > from trac.core import Component, TracError, implements > @@ -861,8 +860,7 @@ class KeywordSuggestModule(Component): > self.log.debug(""" > No keywords found. KeywordSuggestPlugin is disabled.""") > keywords = [] > - # data = {'keywords': keywords} > - # add_script_data(req, data) > + > if filename == 'bh_ticket.html': > # add_script(req, 'theme/js/keywordsuggest_ticket.js') > if req.path_info.startswith('/ticket/'): > @@ -890,61 +888,10 @@ class KeywordSuggestModule(Component): > });""" > > if filename == 'bh_query.html': > - js = """jQuery(document).ready(function ($) { > - function addAutocompleteBehavior() { > - var filters = $('#filters'); > - var contains = $.contains // jQuery 1.4+ > - || function (container, contained) { > - while (contained !== null) { > - if (container === contained) > - return true; > - contained = contained.parentNode; > - } > - return false; > - }; > - var listener = function (event) { > - var target = event.target || > event.srcElement; > - filters.each(function () { > - if (contains(this, target)) { > - var input = > $(this).find('input:text').filter(function () { > - return target === this; > - }); > - var name = input.attr('name'); > - if (input.attr('autocomplete') !== > 'off' && > - > /^(?:[0-9]+_)?(?:keywords)$/.test(name)) { > - input.tagsinput({ > - typeahead: { > - source: %(keywords)s > - } > - }); > - input.focus(); // XXX Workaround for > Trac 0.12.2 and jQuery 1.4.2 > - } > - } > - }); > - }; > - if ($.fn.on) { > - // delegate method is available in jQuery > 1.7+ > - filters.on('focusin', 'input:text', > listener); > - } > - else if ($.fn.delegate) { > - // delegate method is available in jQuery > 1.4.2+ > - filters.delegate('input:text', 'focus', > listener); > - } > - else if (window.addEventListener) { > - // use capture=true cause focus event > doesn't bubble in the default > - filters.each(function () { > - this.addEventListener('focus', listener, > true); > - }); > - } > - else { > - // focusin event bubbles, the event is > avialable for IE only > - filters.each(function () { > - this.attachEvent('onfocusin', listener); > - }); > - } > - } > - addAutocompleteBehavior(); > - });""" > + data = {'keywords': keywords} > + add_script_data(req, data) > + add_script(req, 'theme/js/keywordsuggest_query.js') > + > # inject transient part of javascript directly into ticket.html > template > if req.path_info.startswith('/ticket/') or \ > req.path_info.startswith('/newticket'): > @@ -952,9 +899,6 @@ class KeywordSuggestModule(Component): > 'keywords': keywords > } > stream = stream | > Transformer('.//head').append(tag.script(Markup(js_ticket), > type='text/javascript')) > - if req.path_info.startswith('/query'): > - js_ticket = js % {'keywords': keywords} > - stream = stream | > Transformer('.//head').append(tag.script(Markup(js_ticket), > type='text/javascript')) > > return stream > > @@ -964,29 +908,30 @@ class KeywordSuggestModule(Component): > """ > #currently all the keywords are taken through the db query and > then sort them according to there frequency > # get keywords from db > - db = self.env.get_db_cnx() > - cursor = db.cursor() > keywords = [] > - if self.env.product is not None: > - product = self.env.product._data['prefix'] > - product_sql = " AND t.product = '%s'" % product > - else: > - product_sql = "" > - sql = """SELECT t.keywords FROM ticket AS t WHERE t.keywords IS > NOT null%s""" % product_sql > + with self.env.db_direct_query as db: > + cursor = db.cursor() > + > + if self.env.product is not None: > + product = self.env.product._data['prefix'] > + product_sql = " AND t.product = '%s'" % product > + else: > + product_sql = "" > + sql = """SELECT t.keywords FROM ticket AS t WHERE t.keywords > IS NOT null%s""" % product_sql > > - cursor.execute(sql) > + cursor.execute(sql) > > - for row in cursor: > - if not row[0] == '': > - row_val = str(row[0]).split(',') > - for val in row_val: > - keywords.append(val.strip()) > - # sort keywords according to frequency of occurrence > - if keywords: > - keyword_dic = Counter(keywords) > - keywords = sorted(keyword_dic, key=lambda key: > -keyword_dic[key]) > - else: > - keywords = '' > + for row in cursor: > + if not row[0] == '': > + row_val = str(row[0]).split(',') > + for val in row_val: > + keywords.append(val.strip()) > + # sort keywords according to frequency of occurrence > + if keywords: > + keyword_dic = {keyword: keywords.count(keyword) for > keyword in keywords} > + keywords = sorted(keyword_dic, key=lambda key: > -keyword_dic[key]) > + else: > + keywords = '' > > return keywords > > @@ -1001,6 +946,7 @@ class DuplicateTicketSearch(Component): > # ITemplateStreamFilter methods > > def filter_stream(self, req, method, filename, stream, data): > + > add_script(req, 'theme/js/popoverDupSearch.js') > > if filename == 'bh_ticket.html': > @@ -1015,15 +961,17 @@ class DuplicateTicketSearch(Component): > def match_request(self, req): > """Handle requests sent to /user_list and /ticket/user_list > """ > - return req.path_info.rstrip('/') == '/duplicate_ticket_search' > + return re.match('.*/duplicate_ticket_search$', req.path_info) > > def process_request(self, req): > - terms = req.args.get('q').split(' ') > + > + terms = self._terms_to_search(req) > + term_split = req.args.get('q').split(' ') > > with self.env.db_direct_query as db: > - sql, args = self._search_to_sql(db, ['summary', 'keywords', > 'description'], terms) > - sql2, args2 = self._search_to_sql(db, ['newvalue'], terms) > - sql3, args3 = self._search_to_sql(db, ['value'], terms) > + sql, args = self._sql_to_search(db, ['summary', 'keywords', > 'description'], terms) > + sql2, args2 = self._sql_to_search(db, ['newvalue'], terms) > + sql3, args3 = self._sql_to_search(db, ['value'], terms) > if self.env.product is not None: > product_sql = "product='%s' AND" % > self.env.product._data['prefix'] > else: > @@ -1048,9 +996,9 @@ class DuplicateTicketSearch(Component): > summary_term_count = 0 > summary_list = summary.split(' ') > for s in summary_list: > - for t in terms: > + for t in term_split: > if s.lower() == t.lower(): > - summary = summary.replace(s,'<em>'+t+'</em>') > + summary = summary.replace(s, '<em>'+t+'</em>') > summary_term_count += 1 > break > > @@ -1064,7 +1012,7 @@ class DuplicateTicketSearch(Component): > > # Private methods > > - def _search_to_sql(self, db, columns, terms): > + def _sql_to_search(self, db, columns, terms): > """Convert a search query into an SQL WHERE clause and > corresponding > parameters. > > @@ -1080,17 +1028,22 @@ class DuplicateTicketSearch(Component): > args.extend(['%' + db.like_escape(t) + '%'] * len(columns)) > return sql, tuple(args) > > + def _terms_to_search(self, req): > + """Convert a search query into different terms to search. > > + The result is returned as a list of terms. > + """ > + search_string = req.args.get('q') > + terms = [search_string] > + temp_string = search_string > + search_string_split = search_string.split(' ') > + > + for i in range(len(search_string_split)-3): > + search_string = re.sub('^'+search_string_split[i]+' ', '', > search_string) > + terms.append(search_string) > + search_string = temp_string > + for i in reversed(xrange(3, len(search_string_split))): > + search_string = re.sub(' '+search_string_split[i]+'$', '', > search_string) > + terms.append(search_string) > > - > - > - > - > - > - > - > - > - > - > - > - > + return terms > \ No newline at end of file > > > -- *Thimal Kempitiya <http://www.facebook.com/thimalk> UndergraduateDepartment of Computer Science and Engineering University of Moratuwa.*
