Odd Simon Simonsen avatar Odd Simon Simonsen committed e9ee9e9 Merge

Merged.

Comments (0)

Files changed (1)

new_group_format.diff

 # HG changeset patch
-# Parent bf61e03008de3ac54ea554fde74960b5bec6d514
+# Parent 681a3fa63d270d2007d45f380cb122695c262eda
 
-diff -r bf61e03008de trac/ticket/query.py
---- a/trac/ticket/query.py	Sun Dec 18 14:43:29 2011 +0100
-+++ b/trac/ticket/query.py	Sun Dec 18 17:23:05 2011 +0100
+diff -r 681a3fa63d27 trac/ticket/query.py
+--- a/trac/ticket/query.py	Sun Dec 18 21:44:21 2011 +0100
++++ b/trac/ticket/query.py	Sun Dec 18 21:51:49 2011 +0100
 @@ -48,6 +48,29 @@
  from trac.wiki.api import IWikiSyntaxProvider
  from trac.wiki.macros import WikiMacroBase # TODO: should be moved in .api
 +    
 +def field_to_string(d):
 +    try:
-+        s = '+'
++        s = ''
 +        if d['desc']:
 +            s = '-'
 +        s += d['field']
 +        pass
 +    return None
 +
-+def field_from_string(s=''):
++def field_from_string(s):
 +    try:
 +        if s[0] == '-':
-+            return {'field': s[1:], 'desc': 1}
++            return {'field': s[1:], 'desc': True}
 +        elif s[0] == '+':
-+            return {'field': s[1:], 'desc': 0}
++            return {'field': s[1:], 'desc': False}
 +        else:
-+            return {'field': s, 'desc': 0}
++            return {'field': s, 'desc': False}
 +    except (IndexError, TypeError), e:
 +        pass
 +    return None
  
  class QuerySyntaxError(TracError):
      """Exception raised when a ticket query cannot be parsed from a string."""
-@@ -66,7 +89,7 @@
+@@ -65,7 +88,7 @@
+     clause_re = re.compile(r'(?P<clause>\d+)_(?P<field>.+)$')
  
      def __init__(self, env, report=None, constraints=None, cols=None,
-                  order=None, desc=0, group=None, groupdesc=0, verbose=0,
--                 rows=None, page=None, max=None, format=None):
-+                 rows=None, page=None, max=None, format=None, new_group=None):
+-                 order=None, desc=0, group=None, groupdesc=0, verbose=0,
++                 order=None, desc=0, group=None, groupdesc=None, verbose=0,
+                  rows=None, page=None, max=None, format=None):
          self.env = env
          self.id = report # if not None, it's the corresponding saved query
-         constraints = constraints or []
-@@ -76,11 +99,17 @@
+@@ -76,8 +99,6 @@
          synonyms = TicketSystem(self.env).get_field_synonyms()
          self.order = synonyms.get(order, order)     # 0.11 compatibility
          self.desc = desc
          self.format = format
          self.default_page = 1
          self.items_per_page = QueryModule(self.env).items_per_page
-+        
-+        if not new_group:
-+            if group or groupdesc: # 0.12 compatibility
-+                new_group = field_to_string(dict({'field': group, 'desc': groupdesc}))
-+            else:
-+                new_group = field_to_string(dict({'field': None, 'desc': 0}))
-+        
-+        self.new_group = field_from_string(new_group)
- 
-         # getting page number (default_page if unspecified)
-         if not page:
-@@ -142,18 +171,13 @@
+@@ -142,18 +163,20 @@
                  (self.order, self.desc) = ('priority', 0)
  
          # Handle empty string as a request to not group the result
 -        if self.group != '' and self.group not in field_names:
-+        if self.new_group is None or (self.new_group.get('field') != '' and self.new_group.get('field') not in field_names):
-             default_group = QueryModule(self.env).default_group
-             if default_group:
+-            default_group = QueryModule(self.env).default_group
+-            if default_group:
 -                if default_group[0] == '-':
 -                    (self.group, self.groupdesc) = (default_group[1:], 1)
 -                elif default_group[0] == '+':
 -                    (self.group, self.groupdesc) = (default_group[1:], 0)
 -                else:
 -                    (self.group, self.groupdesc) = (default_group, 0)
-+                self.new_group = field_from_string(default_group)
-             
+-            
 -            if self.group not in field_names:
 -                (self.group, self.groupdesc) = (None, 0)
-+            if not self.new_group or self.new_group.get('field') not in field_names:
-+                self.new_group = None
++        if group == '':
++            self.group = group
++        else:
++            self.group = field_from_string(group)
++            if self.group is not None and groupdesc is not None: # 0.12 compatibility
++                self.group['desc'] = True if groupdesc else False
++
++            if self.group is None or (self.group.get('field') != '' and self.group.get('field') not in field_names):
++                default_group = QueryModule(self.env).default_group
++                if default_group:
++                    self.group = field_from_string(default_group)
++                
++                if not self.group or self.group.get('field') not in field_names:
++                    self.group = None
  
          constraint_cols = {}
          for clause in self.constraints:
-@@ -169,7 +193,7 @@
-     
-     @classmethod
-     def from_string(cls, env, string, **kw):
--        kw_strs = ['order', 'group', 'page', 'max', 'format']
-+        kw_strs = ['order', 'group', 'new_group', 'page', 'max', 'format']
-         kw_arys = ['rows']
-         kw_bools = ['desc', 'groupdesc', 'verbose']
-         kw_synonyms = {'row': 'rows'}
-@@ -293,8 +317,8 @@
+@@ -293,8 +316,8 @@
              cols.append(self.order)
  
          # Make sure to not show the column we group on.
 -        if self.group and self.group in cols:
 -            cols.remove(self.group)
-+        if self.new_group and self.new_group.get('name') in cols:
-+            cols.remove(self.new_group.get('name'))
++        if self.group and self.group.get('field') in cols:
++            cols.remove(self.group.get('field'))
  
          return cols
  
-@@ -336,7 +360,7 @@
- 
-             if self.has_more_pages:
-                 max = self.max
--                if self.group:
-+                if self.new_group:
-                     max += 1
-                 sql = sql + " LIMIT %d OFFSET %d" % (max, self.offset)
-                 if (self.page > int(ceil(float(self.num_items) / self.max)) and
-@@ -434,8 +458,7 @@
+@@ -434,8 +457,7 @@
          return href.query(constraints,
                            report=id,
                            order=order, desc=1 if desc else None,
 -                          group=self.group,
 -                          groupdesc=1 if self.groupdesc else None,
-+                          new_group=self.new_group,
++                          group=field_to_string(self.group),
                            col=cols,
                            row=self.rows,
                            max=max,
-@@ -461,6 +484,9 @@
+@@ -461,6 +483,7 @@
              locale = req.locale
          self.get_columns()
          db = self.env.get_read_db()
-+        group_field = None
-+        if self.new_group:
-+            group_field = self.new_group.get('field')
++        group_field = self.group.get('field') if self.group else None
  
          enum_columns = ('resolution', 'priority', 'severity')
          # Build the list of actual columns to query
-@@ -469,8 +495,8 @@
+@@ -469,8 +492,8 @@
              for col in args:
                  if not col in cols:
                      cols.append(col)
          if self.rows:
              add_cols('reporter', *self.rows)
          add_cols('status', 'priority', 'time', 'changetime', self.order)
-@@ -494,14 +520,14 @@
+@@ -494,14 +517,14 @@
  
          # Join with the enum table for proper sorting
          for col in [c for c in enum_columns
 -                    if c == self.order or c == self.group or c == 'priority']:
-+                    if c == self.order or c == group_field or c == 'priority']:
++                    if c in (self.order, group_field, 'priority')]:
              sql.append("\n  LEFT OUTER JOIN enum AS %s ON "
                         "(%s.type='%s' AND %s.name=%s)"
                         % (col, col, col, col, col))
          # Join with the version/milestone tables for proper sorting
          for col in [c for c in ['milestone', 'version']
 -                    if c == self.order or c == self.group]:
-+                    if c == self.order or c == group_field]:
++                    if c in (self.order, group_field)]:
              sql.append("\n  LEFT OUTER JOIN %s ON (%s.name=%s)"
                         % (col, col, col))
  
-@@ -653,8 +679,8 @@
+@@ -653,8 +676,8 @@
              
          sql.append("\nORDER BY ")
          order_cols = [(self.order, self.desc)]
 -        if self.group and self.group != self.order:
 -            order_cols.insert(0, (self.group, self.groupdesc))
-+        if self.new_group and self.new_group.get('field') != self.order:
-+            order_cols.insert(0, (self.new_group.get('field'), self.new_group.get('desc')))
++        if self.group and self.group.get('field') != self.order:
++            order_cols.insert(0, (self.group.get('field'), self.group.get('desc')))
  
          for name, desc in order_cols:
              if name in enum_columns:
-@@ -684,7 +710,7 @@
+@@ -684,7 +707,7 @@
                             % (desc, desc, col, desc))
              else:
                  sql.append("%s%s" % (col, desc))
                  sql.append(",")
          if self.order != 'id':
              sql.append(",t.id")  
-@@ -781,8 +807,8 @@
-                     ticket['added'] = True
+@@ -782,7 +805,7 @@
                  elif ticket['changetime'] > orig_time:
                      ticket['changed'] = True
--            if self.group:
+             if self.group:
 -                group_key = ticket[self.group]
-+            if self.new_group:
-+                group_key = ticket[self.new_group.get('field')]
++                group_key = ticket[self.group.get('field')]
                  groups.setdefault(group_key, []).append(ticket)
                  if not groupsequence or group_key not in groupsequence:
                      groupsequence.append(group_key)
-@@ -941,12 +967,12 @@
+@@ -941,12 +964,12 @@
                  constraints = self._get_constraints(arg_list=arg_list)
              else:
                  query = Query.from_string(self.env, qstring)
 -                args = {'order': query.order, 'group': query.group,
 -                        'col': query.cols, 'max': query.max}
 +                args = {'order': query.order,
-+                        'group': query.new_group,
++                        'group': field_to_string(query.group),
 +                        'col': query.cols,
 +                        'max': query.max}
                  if query.desc:
                  constraints = query.constraints
  
              # Substitute $USER, or ensure no field constraints that depend
-@@ -974,13 +1000,12 @@
+@@ -960,6 +983,12 @@
+                             del clause[field]
+                             break
+ 
++        group = field_from_string(args.get('group'))
++        if group is not None:
++            if 'groupdesc' in args:
++                group['desc'] = True if args.get('groupdesc') else False
++                args['group'] = field_to_string(group)
++        
+         cols = args.get('col')
+         if isinstance(cols, basestring):
+             cols = [cols]
+@@ -974,13 +1003,12 @@
          max = args.get('max')
          if max is None and format in ('csv', 'tab'):
              max = 0 # unlimited unless specified explicitly
 +        query = Query(self.env, report=req.args.get('report'),
 +                      constraints=constraints, cols=cols,
 +                      order=args.get('order'), desc='desc' in args,
++                      group=args.get('group'),
 +                      verbose='verbose' in args, rows=rows,
-+                      page=args.get('page'), max=max,
-+                      new_group=field_to_string(args.get('group')))
++                      page=args.get('page'), max=max)
  
          if 'update' in req.args:
              # Reset session vars
-@@ -1367,9 +1392,9 @@
- 
-             def query_href(extra_args, group_value = None):
-                 q = Query.from_string(self.env, query_string)
--                if q.group:
--                    extra_args[q.group] = group_value
--                    q.group = None
-+                if q.new_group:
-+                    extra_args[q.new_group] = group_value
-+                    q.new_group = None
-                 for constraint in q.constraints:
-                     constraint.update(extra_args)
-                 if not q.constraints:
-@@ -1378,7 +1403,7 @@
-             chrome = Chrome(self.env)
-             tickets = apply_ticket_permissions(self.env, req, tickets)
-             stats_provider = RoadmapModule(self.env).stats_provider
--            by = query.group
-+            by = query.new_group
-             if not by:
-                 stat = get_ticket_stats(stats_provider, tickets)
-                 data = {
-@@ -1434,25 +1459,25 @@
+@@ -1434,18 +1462,18 @@
  
          def ticket_groups():
              groups = []
 -            for v, g in groupby(tickets, lambda t: t[query.group]):
-+            for v, g in groupby(tickets, lambda t: t[query.new_group.get('field')]):
++            for v, g in groupby(tickets, lambda t: t[query.group.get('field')]):
                  q = Query.from_string(self.env, query_string)
                  # produce the hint for the group
 -                q.group = q.groupdesc = None
-+                q.new_group = None
++                q.group = None
                  order = q.order
                  q.order = None
                  title = _("%(groupvalue)s %(groupname)s tickets matching "
 -                          "%(query)s", groupvalue=v, groupname=query.group,
-+                          "%(query)s", groupvalue=v, groupname=query.new_group.get('field'),
++                          "%(query)s", groupvalue=v, groupname=query.group.get('field'),
                            query=q.to_string())
                  # produce the href for the query corresponding to the group
                  for constraint in q.constraints:
 -                    constraint[str(query.group)] = v
-+                    constraint[str(query.new_group.get('field'))] = v
++                    constraint[str(query.group.get('field'))] = v
                  q.order = order
                  href = q.get_href(formatter.context)
                  groups.append((v, [t for t in g], href, title))
-             return groups
- 
-         if format == 'compact':
--            if query.group:
-+            if query.new_group:
-                 groups = [(v, ' ', 
-                            tag.a('#%s' % ','.join([str(t['id']) for t in g]),
-                                  href=href, class_='query', title=title))
-@@ -1462,12 +1487,12 @@
-                 alist = [ticket_anchor(ticket) for ticket in tickets]
-                 return tag.span(alist[0], *[(', ', a) for a in alist[1:]])
-         else:
--            if query.group:
-+            if query.new_group:
-                 return tag.div(
+@@ -1467,7 +1495,7 @@
                      [(tag.p(tag_('%(groupvalue)s %(groupname)s tickets:',
                                   groupvalue=tag.a(v, href=href, class_='query',
                                                    title=title),
 -                                 groupname=query.group)),
-+                                 groupname=query.new_group.get('field'))),
++                                 groupname=query.group.get('field'))),
                        tag.dl([(tag.dt(ticket_anchor(t)),
                                 tag.dd(t['summary'])) for t in g],
                               class_='wiki compact'))
-diff -r bf61e03008de trac/ticket/templates/query.html
---- a/trac/ticket/templates/query.html	Sun Dec 18 14:43:29 2011 +0100
-+++ b/trac/ticket/templates/query.html	Sun Dec 18 17:23:05 2011 +0100
+diff -r 681a3fa63d27 trac/ticket/templates/query.html
+--- a/trac/ticket/templates/query.html	Sun Dec 18 21:44:21 2011 +0100
++++ b/trac/ticket/templates/query.html	Sun Dec 18 21:51:49 2011 +0100
 @@ -184,12 +184,12 @@
              <option></option>
              <py:for each="field_name in field_names" py:with="field = fields[field_name]">
                <option py:if="field.type in ('select', 'radio') or field_name in ('owner', 'reporter')"
 -                      selected="${field_name == query.group or None}"
-+                      selected="${if query.new_group and field_name == query.new_group.get('field') else None}"
++                      selected="${query.group and field_name == query.group.get('field') or None}"
                        value="${field_name}">${field.label}</option>
              </py:for>
            </select>
            <input type="checkbox" name="groupdesc" id="groupdesc"
 -                 checked="${query.groupdesc or None}" />
-+                 checked="${query.new_group.get('desc') if query.new_group else None}" />
++                 checked="${query.group and query.group.get('desc') or None}" />
            <label for="groupdesc">descending</label>
          </p>
  
-diff -r bf61e03008de trac/ticket/templates/query_results.html
---- a/trac/ticket/templates/query_results.html	Sun Dec 18 14:43:29 2011 +0100
-+++ b/trac/ticket/templates/query_results.html	Sun Dec 18 17:23:05 2011 +0100
+diff -r 681a3fa63d27 trac/ticket/templates/query_results.html
+--- a/trac/ticket/templates/query_results.html	Sun Dec 18 21:44:21 2011 +0100
++++ b/trac/ticket/templates/query_results.html	Sun Dec 18 21:51:49 2011 +0100
 @@ -24,8 +24,8 @@
    <py:def function="group_heading(groupname, results)">
      <h2 class="report-result" py:if="groupname is not None"
          i18n:msg="grouplabel, groupname, count"
 -        py:with="grouplabel = fields[query.group].label;
 -                 groupname = authorinfo(groupname) if query.group in ['owner', 'reporter'] else (groupname or _('None'));
-+        py:with="grouplabel = fields[query.new_group.get('field')].label;
-+                 groupname = authorinfo(groupname) if query.new_group and query.new_group.get('field') in ['owner', 'reporter'] else (groupname or _('None'));
++        py:with="grouplabel = fields[query.group.get('field')].label;
++                 groupname = authorinfo(groupname) if query.group and query.group.get('field') in ['owner', 'reporter'] else (groupname or _('None'));
                   count = ngettext('%(num)s match', '%(num)s matches', len(results))">
        ${grouplabel}: ${groupname} <span class="numrows">(${count})</span>
      </h2>
-diff -r bf61e03008de trac/ticket/tests/query.py
---- a/trac/ticket/tests/query.py	Sun Dec 18 14:43:29 2011 +0100
-+++ b/trac/ticket/tests/query.py	Sun Dec 18 17:23:05 2011 +0100
-@@ -176,7 +176,7 @@
+diff -r 681a3fa63d27 trac/ticket/tests/query.py
+--- a/trac/ticket/tests/query.py	Sun Dec 18 21:44:21 2011 +0100
++++ b/trac/ticket/tests/query.py	Sun Dec 18 21:51:49 2011 +0100
+@@ -172,6 +172,17 @@
+         self.assertEqual([], args)
+         tickets = query.execute(self.req)
+ 
++        query = Query(self.env, order='id', group='-milestone')
++        sql, args = query.get_sql()
++        self.assertEqualSQL(sql,
++"""SELECT t.id AS id,t.summary AS summary,t.owner AS owner,t.type AS type,t.status AS status,t.priority AS priority,t.milestone AS milestone,t.time AS time,t.changetime AS changetime,priority.value AS priority_value
++FROM ticket AS t
++  LEFT OUTER JOIN enum AS priority ON (priority.type='priority' AND priority.name=priority)
++  LEFT OUTER JOIN milestone ON (milestone.name=milestone)
++ORDER BY COALESCE(t.milestone,'')='' DESC,COALESCE(milestone.completed,0)=0 DESC,milestone.completed DESC,COALESCE(milestone.due,0)=0 DESC,milestone.due DESC,t.milestone DESC,COALESCE(t.id,0)=0,t.id""")
++        self.assertEqual([], args)
++        tickets = query.execute(self.req)
++
+     def test_grouped_by_priority(self):
          query = Query(self.env, group='priority')
          sql, args = query.get_sql()
-         self.assertEqualSQL(sql,
--"""SELECT t.id AS id,t.summary AS summary,t.owner AS owner,t.type AS type,t.status AS status,t.milestone AS milestone,t.priority AS priority,t.time AS time,t.changetime AS changetime,priority.value AS priority_value
-+"""SELECT t.id AS id,t.summary AS summary,t.owner AS owner,t.type AS type,t.status AS status,t.priority AS priority,t.milestone AS milestone,t.time AS time,t.changetime AS changetime,priority.value AS priority_value
- FROM ticket AS t
-   LEFT OUTER JOIN enum AS priority ON (priority.type='priority' AND priority.name=priority)
- ORDER BY COALESCE(priority.value,'')='',%(cast_priority)s,t.id""" % {
-@@ -503,7 +503,7 @@
-         query = Query(self.env, group='summary')
-         sql, args = query.get_sql()
-         self.assertEqualSQL(sql,
--"""SELECT t.id AS id,t.owner AS owner,t.type AS type,t.status AS status,t.priority AS priority,t.milestone AS milestone,t.summary AS summary,t.time AS time,t.changetime AS changetime,priority.value AS priority_value
-+"""SELECT t.id AS id,t.summary AS summary,t.owner AS owner,t.type AS type,t.status AS status,t.priority AS priority,t.milestone AS milestone,t.time AS time,t.changetime AS changetime,priority.value AS priority_value
- FROM ticket AS t
-   LEFT OUTER JOIN enum AS priority ON (priority.type='priority' AND priority.name=priority)
- ORDER BY COALESCE(t.summary,'')='',t.summary,COALESCE(priority.value,'')='',CAST(priority.value AS integer),t.id""")
-@@ -527,7 +527,7 @@
-         query = Query(self.env, order='component', group='milestone')
-         sql, args = query.get_sql()
-         self.assertEqualSQL(sql,
--"""SELECT t.id AS id,t.summary AS summary,t.owner AS owner,t.type AS type,t.status AS status,t.priority AS priority,t.component AS component,t.milestone AS milestone,t.time AS time,t.changetime AS changetime,priority.value AS priority_value
-+"""SELECT t.id AS id,t.summary AS summary,t.owner AS owner,t.type AS type,t.status AS status,t.priority AS priority,t.milestone AS milestone,t.component AS component,t.time AS time,t.changetime AS changetime,priority.value AS priority_value
- FROM ticket AS t
-   LEFT OUTER JOIN enum AS priority ON (priority.type='priority' AND priority.name=priority)
-   LEFT OUTER JOIN milestone ON (milestone.name=milestone)
-@@ -541,7 +541,7 @@
-         query = Query(self.env)
-         sql, args = query.get_sql()
-         self.assertEqualSQL(sql,
--"""SELECT t.id AS id,t.summary AS summary,t.owner AS owner,t.type AS type,t.priority AS priority,t.milestone AS milestone,t.status AS status,t.time AS time,t.changetime AS changetime,priority.value AS priority_value
-+"""SELECT t.id AS id,t.summary AS summary,t.owner AS owner,t.type AS type,t.status AS status,t.priority AS priority,t.milestone AS milestone,t.time AS time,t.changetime AS changetime,priority.value AS priority_value
- FROM ticket AS t
-   LEFT OUTER JOIN enum AS priority ON (priority.type='priority' AND priority.name=priority)
- ORDER BY COALESCE(t.status,'')='',t.status,COALESCE(priority.value,'')='',CAST(priority.value AS integer),t.id""")
+diff -r 681a3fa63d27 trac/ticket/tests/wikisyntax.py
+--- a/trac/ticket/tests/wikisyntax.py	Sun Dec 18 21:44:21 2011 +0100
++++ b/trac/ticket/tests/wikisyntax.py	Sun Dec 18 21:51:49 2011 +0100
+@@ -235,7 +235,7 @@
+ <a class="query" href="/query?group=owner&amp;order=priority">query:group=owner</a>
+ </p>
+ <p>
+-<a class="query" href="/query?order=priority&amp;row=description">query:verbose=1</a>
++<a class="query" href="/query?row=description&amp;order=priority">query:verbose=1</a>
+ </p>
+ <p>
+ <a class="query" href="/query?summary=r%C3%A9sum%C3%A9&amp;order=priority">query:summary=résumé</a>
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.