Project import generated by Copybara.
GitOrigin-RevId: d9e9e3fb4e31372ec1fb43b178994ca78fa8fe70
diff --git a/tracker/issueadvsearch.py b/tracker/issueadvsearch.py
new file mode 100644
index 0000000..d824098
--- /dev/null
+++ b/tracker/issueadvsearch.py
@@ -0,0 +1,123 @@
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file or at
+# https://developers.google.com/open-source/licenses/bsd
+
+"""Classes that implement the advanced search feature page.
+
+The advanced search page simply displays an HTML page with a form.
+The form handler converts the widget-based query into a googley query
+string and redirects the user to the issue list servlet.
+"""
+from __future__ import print_function
+from __future__ import division
+from __future__ import absolute_import
+
+import logging
+import re
+
+from features import savedqueries_helpers
+from framework import framework_helpers
+from framework import permissions
+from framework import servlet
+from framework import urls
+
+# Patterns for search values that can be words, labels,
+# component paths, or email addresses.
+VALUE_RE = re.compile(r'[-a-zA-Z0-9._>@]+')
+
+
+class IssueAdvancedSearch(servlet.Servlet):
+ """IssueAdvancedSearch shows a form to enter an advanced search."""
+
+ _PAGE_TEMPLATE = 'tracker/issue-advsearch-page.ezt'
+ _MAIN_TAB_MODE = servlet.Servlet.MAIN_TAB_ISSUES
+
+ # This form *only* redirects to a GET request, and permissions are checked
+ # in that handler.
+ CHECK_SECURITY_TOKEN = False
+
+ def GatherPageData(self, mr):
+ """Build up a dictionary of data values to use when rendering the page.
+
+ Args:
+ mr: commonly used info parsed from the request.
+
+ Returns:
+ Dict of values used by EZT for rendering the page.
+ """
+ # TODO(jrobbins): Allow deep-linking into this page.
+ canned_query_views = []
+ if mr.project_id:
+ with mr.profiler.Phase('getting canned queries'):
+ canned_queries = self.services.features.GetCannedQueriesByProjectID(
+ mr.cnxn, mr.project_id)
+ canned_query_views = [
+ savedqueries_helpers.SavedQueryView(sq, idx + 1, None, None)
+ for idx, sq in enumerate(canned_queries)]
+
+ saved_query_views = []
+ if mr.auth.user_id and self.services.features:
+ with mr.profiler.Phase('getting saved queries'):
+ saved_queries = self.services.features.GetSavedQueriesByUserID(
+ mr.cnxn, mr.me_user_id)
+ saved_query_views = [
+ savedqueries_helpers.SavedQueryView(sq, idx + 1, None, None)
+ for idx, sq in enumerate(saved_queries)
+ if (mr.project_id in sq.executes_in_project_ids or
+ not mr.project_id)]
+
+ return {
+ 'issue_tab_mode': 'issueAdvSearch',
+ 'page_perms': self.MakePagePerms(mr, None, permissions.CREATE_ISSUE),
+ 'canned_queries': canned_query_views,
+ 'saved_queries': saved_query_views,
+ }
+
+ def ProcessFormData(self, mr, post_data):
+ """Process a posted advanced query form.
+
+ Args:
+ mr: commonly used info parsed from the request.
+ post_data: HTML form data from the request.
+
+ Returns:
+ String URL to redirect the user to after processing.
+ """
+ # Default to searching open issues in this project.
+ can = post_data.get('can', 2)
+
+ terms = []
+ self._AccumulateANDTerm('', 'words', post_data, terms)
+ self._AccumulateANDTerm('-', 'without', post_data, terms)
+ self._AccumulateANDTerm('label:', 'labels', post_data, terms)
+ self._AccumulateORTerm('component:', 'components', post_data, terms)
+ self._AccumulateORTerm('status:', 'statuses', post_data, terms)
+ self._AccumulateORTerm('reporter:', 'reporters', post_data, terms)
+ self._AccumulateORTerm('owner:', 'owners', post_data, terms)
+ self._AccumulateORTerm('cc:', 'cc', post_data, terms)
+ self._AccumulateORTerm('commentby:', 'commentby', post_data, terms)
+
+ if 'starcount' in post_data:
+ starcount = int(post_data['starcount'])
+ if starcount >= 0:
+ terms.append('starcount:%s' % starcount)
+
+ return framework_helpers.FormatAbsoluteURL(
+ mr, urls.ISSUE_LIST, q=' '.join(terms), can=can)
+
+ def _AccumulateANDTerm(self, operator, form_field, post_data, search_query):
+ """Build a query that matches issues with ALL of the given field values."""
+ user_input = post_data.get(form_field)
+ if user_input:
+ values = VALUE_RE.findall(user_input)
+ search_terms = ['%s%s' % (operator, v) for v in values]
+ search_query.extend(search_terms)
+
+ def _AccumulateORTerm(self, operator, form_field, post_data, search_query):
+ """Build a query that matches issues with ANY of the given field values."""
+ user_input = post_data.get(form_field)
+ if user_input:
+ values = VALUE_RE.findall(user_input)
+ search_term = '%s%s' % (operator, ','.join(values))
+ search_query.append(search_term)