Switch to side-by-side view

--- a/ForgeTracker/forgetracker/tracker_main.py
+++ b/ForgeTracker/forgetracker/tracker_main.py
@@ -306,6 +306,33 @@
     def bins(self):
         return TM.Bin.query.find(dict(app_config_id=self.config._id)).sort('summary').all()
 
+
+
+
+
+### Controllers ###
+
+def mongo_columns():
+    columns = [dict(name='ticket_num', sort_name='ticket_num', label='Ticket Number', active=True),
+               dict(name='summary', sort_name='summary', label='Summary', active=True),
+               dict(name='_milestone', sort_name='custom_fields._milestone', label='Milestone', active=True),
+               dict(name='status', sort_name='status', label='Status', active=True),
+               dict(name='assigned_to', sort_name='assigned_to_username', label='Owner', active=True)]
+    for field in c.app.globals.sortable_custom_fields_shown_in_search():
+        columns.append(
+            dict(name=field['name'], sort_name=field['name'], label=field['label'], active=True))
+    return columns
+
+def solr_columns():
+    columns = [dict(name='ticket_num', sort_name='ticket_num_i', label='Ticket Number', active=True),
+               dict(name='summary', sort_name='snippet_s', label='Summary', active=True),
+               dict(name='_milestone', sort_name='_milestone_s', label='Milestone', active=True),
+               dict(name='status', sort_name='status_s', label='Status', active=True),
+               dict(name='assigned_to', sort_name='assigned_to_s', label='Owner', active=True)]
+    for field in c.app.globals.sortable_custom_fields_shown_in_search():
+        columns.append(dict(name=field['name'], sort_name=field['sortable_name'], label=field['label'], active=True))
+    return columns
+
 class RootController(BaseController):
 
     def __init__(self):
@@ -331,84 +358,17 @@
             bin_counts.append(dict(label=label, count=count))
         return dict(bin_counts=bin_counts)
 
-    def paged_query(self, q, limit=None, page=0, sort=None, columns=None, **kw):
-        """Query tickets, sorting and paginating the result.
-
-        We do the sorting and skipping right in SOLR, before we ever ask
-        Mongo for the actual tickets.  Other keywords for
-        search_artifact (e.g., history) or for SOLR are accepted through
-        kw.  The output is intended to be used directly in templates,
-        e.g., exposed controller methods can just:
-
-            return paged_query(q, ...)
-
-        If you want all the results at once instead of paged you have
-        these options:
-          - don't call this routine, search directly in mongo
-          - call this routine with a very high limit and TEST that
-            count<=limit in the result
-        limit=-1 is NOT recognized as 'all'.  500 is a reasonable limit.
-        """
-
-        limit, page, start = g.handle_paging(limit, page, default=25)
-        count = 0
-        tickets = []
-        refined_sort = sort if sort else 'ticket_num_i asc'
-        if  'ticket_num_i' not in refined_sort:
-            refined_sort += ',ticket_num_i asc'
-        try:
-            if q:
-                matches = search_artifact(
-                    TM.Ticket, q,
-                    rows=limit, sort=refined_sort, start=start, fl='ticket_num_i', **kw)
-            else:
-                matches = None
-            solr_error = None
-        except ValueError, e:
-            solr_error = e.args[0]
-            matches = []
-        if matches:
-            count = matches.hits
-            # ticket_numbers is in sorted order
-            ticket_numbers = [match['ticket_num_i'] for match in matches.docs]
-            # but query, unfortunately, returns results in arbitrary order
-            query = TM.Ticket.query.find(dict(app_config_id=c.app.config._id, ticket_num={'$in':ticket_numbers}))
-            # so stick all the results in a dictionary...
-            ticket_for_num = {}
-            for t in query:
-                ticket_for_num[t.ticket_num] = t
-            # and pull them out in the order given by ticket_numbers
-            tickets = []
-            for tn in ticket_numbers:
-                if tn in ticket_for_num:
-                    if has_access(ticket_for_num[tn], 'read'):
-                        tickets.append(ticket_for_num[tn])
-                    else:
-                        count = count -1
-        sortable_custom_fields=c.app.globals.sortable_custom_fields_shown_in_search()
-        if not columns:
-            columns = [dict(name='ticket_num', sort_name='ticket_num_i', label='Ticket Number', active=True),
-                       dict(name='summary', sort_name='snippet_s', label='Summary', active=True),
-                       dict(name='_milestone', sort_name='_milestone_s', label='Milestone', active=True),
-                       dict(name='status', sort_name='status_s', label='Status', active=True),
-                       dict(name='assigned_to', sort_name='assigned_to_s', label='Owner', active=True)]
-            for field in sortable_custom_fields:
-                columns.append(dict(name=field['name'], sort_name=field['sortable_name'], label=field['label'], active=True))
-        return dict(tickets=tickets,
-                    sortable_custom_fields=sortable_custom_fields,
-                    columns=columns,
-                    count=count, q=q, limit=limit, page=page, sort=sort,
-                    solr_error=solr_error, **kw)
-
     @with_trailing_slash
     @h.vardec
     @expose('jinja:forgetracker:templates/tracker/index.html')
     def index(self, limit=25, columns=None, page=0, sort='ticket_num desc', **kw):
         kw.pop('q', None) # it's just our original query mangled and sent back to us
-        result = TM.Ticket.paged_query(c.app.globals.not_closed_mongo_query,
+        result = TM.Ticket.paged_query(c.app.config, c.user,
+                                        c.app.globals.not_closed_mongo_query,
                                         sort=sort, limit=int(limit),
-                                        columns=columns, page=page, **kw)
-        c.subscribe_form = W.subscribe_form
+                                        page=page, **kw)
+        result['columns'] = columns or mongo_columns()
+        result['sortable_custom_fields'] = c.app.globals.sortable_custom_fields_shown_in_search()
         result['subscribed'] = M.Mailbox.subscribed()
         result['allow_edit'] = has_access(c.app, 'update')()
         result['help_msg'] = c.app.config.options.get('TicketHelpSearch')
@@ -418,6 +378,7 @@
             sort_split = sort.split(' ')
             solr_col = _mongo_col_to_solr_col(sort_split[0])
             result['url_sort'] = '%s %s' % (solr_col, sort_split[1])
+        c.subscribe_form = W.subscribe_form
         c.ticket_search_results = W.ticket_search_results
         return result
 
@@ -506,7 +467,9 @@
             bin = TM.Bin.query.find(dict(app_config_id=c.app.config._id,terms=q)).first()
         if project:
             redirect(c.project.url() + 'search?' + urlencode(dict(q=q, history=kw.get('history'))))
-        result = self.paged_query(q, page=page, sort=sort, columns=columns, **kw)
+        result = TM.Ticket.paged_search(c.app.config, c.user, q, page=page, sort=sort, **kw)
+        result['columns'] = columns or solr_columns()
+        result['sortable_custom_fields'] = c.app.globals.sortable_custom_fields_shown_in_search()
         result['allow_edit'] = has_access(c.app, 'update')()
         result['bin'] = bin
         result['help_msg'] = c.app.config.options.get('TicketHelpSearch')
@@ -517,23 +480,24 @@
     @h.vardec
     @expose()
     @validate(validators=search_validators)
-    def search_feed(self, q=None, query=None, project=None, columns=None, page=0, sort=None, **kw):
+    def search_feed(self, q=None, query=None, project=None, page=0, sort=None, **kw):
         if query and not q:
             q = query
-        result = self.paged_query(q, page=page, sort=sort, columns=columns, **kw)
+        result = TM.Ticket.paged_search(c.app.config, c.user, q, page=page, sort=sort, **kw)
         response.headers['Content-Type'] = ''
         response.content_type = 'application/xml'
-        d = dict(title='Ticket search results', link=c.app.url, description='You searched for %s' % q, language=u'en')
+        d = dict(title='Ticket search results', link=h.absurl(c.app.url), description='You searched for %s' % q, language=u'en')
         if request.environ['PATH_INFO'].endswith('.atom'):
             feed = FG.Atom1Feed(**d)
         else:
             feed = FG.Rss201rev2Feed(**d)
         for t in result['tickets']:
+            url = h.absurl(t.url().encode('utf-8'))
             feed.add_item(title=t.summary,
-                          link=h.absurl(t.url().encode('utf-8')),
+                          link=url,
                           pubdate=t.mod_date,
                           description=t.description,
-                          unique_id=str(t._id),
+                          unique_id=url,
                           author_name=t.reported_by.display_name,
                           author_link=h.absurl(t.reported_by.url()))
         return feed.writeString('utf-8')
@@ -622,9 +586,11 @@
                    sort=validators.UnicodeString(if_empty='ticket_num_i asc')))
     def edit(self, q=None, limit=None, page=None, sort=None, **kw):
         require_access(c.app, 'update')
-        result = self.paged_query(q, sort=sort, limit=limit, page=page, **kw)
+        result = TM.Ticket.paged_search(c.app.config, c.user, q, sort=sort, limit=limit, page=page, **kw)
         # if c.app.globals.milestone_names is None:
         #     c.app.globals.milestone_names = ''
+        result['columns'] = solr_columns()
+        result['sortable_custom_fields'] = c.app.globals.sortable_custom_fields_shown_in_search()
         result['globals'] = c.app.globals
         result['cancel_href'] = url(
             c.app.url + 'search/',
@@ -1338,10 +1304,14 @@
         require_access(c.app, 'read')
 
     @expose('json:')
-    def index(self, **kw):
-        return dict(tickets=[
-            dict(ticket_num=t.ticket_num, summary=t.summary)
-            for t in TM.Ticket.query.find(dict(app_config_id=c.app.config._id)).sort('ticket_num') ])
+    def index(self, limit=100, page=0, **kw):
+        results = TM.Ticket.paged_query(c.app.config, c.user, query={},
+                                        limit=int(limit), page=int(page))
+        results['tickets'] = [dict(ticket_num=t.ticket_num, summary=t.summary)
+                              for t in results['tickets']]
+        results.pop('q', None)
+        results.pop('sort', None)
+        return results
 
     @expose()
     @h.vardec
@@ -1384,6 +1354,13 @@
         except Exception, e:
             log.exception(e)
             return dict(status=False, errors=[str(e)])
+
+    @expose('json:')
+    def search(self, q=None, limit=100, page=0, sort=None, **kw):
+        results = TM.Ticket.paged_search(c.app.config, c.user, q, limit, page, sort)
+        results['tickets'] = [dict(ticket_num=t.ticket_num, summary=t.summary)
+                              for t in results['tickets']]
+        return results
 
     @expose()
     def _lookup(self, ticket_num, *remainder):
@@ -1445,8 +1422,10 @@
             sort=validators.UnicodeString(if_empty=None)))
     def index(self, q=None, columns=None, page=0, query=None, sort=None, **kw):
         require(has_access(c.app, 'read'))
-        result = TM.Ticket.paged_query(
-            self.mongo_query, page=page, sort=sort, columns=columns, **kw)
+        result = TM.Ticket.paged_query(c.app.config, c.user,
+            self.mongo_query, page=page, sort=sort, **kw)
+        result['columns'] = columns or mongo_columns()
+        result['sortable_custom_fields'] = c.app.globals.sortable_custom_fields_shown_in_search()
         result['allow_edit'] = has_access(c.app, 'update')()
         result['help_msg'] = c.app.config.options.get('TicketHelpSearch')
         progress = c.app.globals.milestone_count(self.progress_key)
@@ -1474,8 +1453,10 @@
                    sort=validators.UnicodeString(if_empty='ticket_num_i asc')))
     def edit(self, q=None, limit=None, page=None, sort=None, columns=None, **kw):
         require_access(c.app, 'update')
-        result = TM.Ticket.paged_query(
-            self.mongo_query, page=page, sort=sort, columns=columns, **kw)
+        result = TM.Ticket.paged_query(c.app.config, c.user,
+            self.mongo_query, page=page, sort=sort, **kw)
+        result['columns'] = columns or mongo_columns()
+        result['sortable_custom_fields'] = c.app.globals.sortable_custom_fields_shown_in_search()
         # if c.app.globals.milestone_names is None:
         #     c.app.globals.milestone_names = ''
         result.pop('q')