source: trunk/plugins/dashboardreportsplugin/dashboardreports/patch/ticket-query.diff @ 302

Revision 302, 11.9 KB checked in by aculapov, 5 years ago (diff)
  • patch after 7218 revision
  • trac/ticket/api.py

     
    182182        for controller in self.action_controllers: 
    183183            valid_states.update(controller.get_all_status()) 
    184184        return sorted(valid_states) 
     185     
     186    def get_all_ticket_fields(self): 
     187        """New method until full integration of the new fields.""" 
     188        fields = self.get_ticket_fields() 
     189         
     190        # append the new ones 
     191        fields.extend([{'name': 'time', 'type': 'date', 'label': 'Created'}, 
     192                       {'name': 'changetime', 'type': 'date',  
     193                            'label': 'Modified'}]) 
     194         
     195        return fields 
    185196 
    186197    def get_ticket_fields(self): 
    187198        """Returns the list of fields available for tickets.""" 
  • trac/ticket/query.py

     
    4343from trac.wiki.api import IWikiSyntaxProvider, parse_args 
    4444from trac.wiki.macros import WikiMacroBase # TODO: should be moved in .api 
    4545 
     46is_op = lambda x: x in ['!', '<', '>'] 
     47 
    4648class QuerySyntaxError(Exception): 
    4749    """Exception raised when a ticket query cannot be parsed from a string.""" 
    4850 
     
    103105            rows = [] 
    104106        if verbose and 'description' not in rows: # 0.10 compatibility 
    105107            rows.append('description') 
    106         self.fields = TicketSystem(self.env).get_ticket_fields() 
     108        self.fields = TicketSystem(self.env).get_all_ticket_fields() 
    107109        field_names = [f['name'] for f in self.fields] 
    108110        self.cols = [c for c in cols or [] if c in field_names or  
    109111                     c in ('id', 'time', 'changetime')] 
     
    123125            self.group = None 
    124126 
    125127    def from_string(cls, env, string, **kw): 
     128        global is_op 
    126129        filters = string.split('&') 
    127130        kw_strs = ['order', 'group', 'page', 'max'] 
    128131        kw_arys = ['rows'] 
     
    132135        for filter_ in filters: 
    133136            filter_ = filter_.split('=') 
    134137            if len(filter_) != 2: 
    135                 raise QuerySyntaxError(_('Query filter requires field and '  
    136                                          'constraints separated by a "="')) 
     138                raise QuerySyntaxError('Query filter requires field and '  
     139                                       'constraints separated by a "="') 
    137140            field,values = filter_ 
    138141            if not field: 
    139                 raise QuerySyntaxError(_('Query filter requires field name')) 
     142                raise QuerySyntaxError('Query filter requires field name') 
    140143            # from last char of `field`, get the mode of comparison 
    141144            mode, neg = '', '' 
    142145            if field[-1] in ('~', '^', '$'): 
    143146                mode = field[-1] 
    144147                field = field[:-1] 
    145             if field[-1] == '!': 
    146                 neg = '!' 
     148            if is_op(field[-1]): 
     149                neg = field[-1] 
    147150                field = field[:-1] 
    148151            processed_values = [] 
    149152            for val in values.split('|'): 
     
    171174    def get_columns(self): 
    172175        if not self.cols: 
    173176            self.cols = self.get_default_columns() 
    174         if not 'id' in self.cols: 
    175             # make sure 'id' is always present (needed for permission checks) 
    176             self.cols.insert(0, 'id')         
    177177        return self.cols 
    178178 
    179179    def get_all_textareas(self): 
     
    187187            if col in cols: 
    188188                cols.remove(col) 
    189189                cols.append(col) 
    190         # TODO: fix after adding time/changetime to the api.py 
    191         cols += ['time', 'changetime'] 
    192190 
    193191        # Semi-intelligently remove columns that are restricted to a single 
    194192        # value by a query constraint. 
     
    251249        return cnt 
    252250 
    253251    def execute(self, req, db=None, cached_ids=None): 
     252        if not self.cols: 
     253            self.get_columns() 
     254 
    254255        if not db: 
    255256            db = self.env.get_db_cnx() 
    256257        cursor = db.cursor() 
     
    279280            fields += [f for f in self.fields if f['name'] == column] or [None] 
    280281        results = [] 
    281282 
    282         column_indices = range(len(columns)) 
    283283        for row in cursor: 
    284             result = {} 
    285             for i in column_indices: 
     284            id = int(row[0]) 
     285            result = {'id': id, 'href': req.href.ticket(id)} 
     286            for i in range(1, len(columns)): 
    286287                name, field, val = columns[i], fields[i], row[i] 
    287288                if name == self.group: 
    288289                    val = val or 'None' 
    289290                elif name == 'reporter': 
    290291                    val = val or 'anonymous' 
    291                 elif name == 'id': 
    292                     val = int(val) 
    293                     result['href'] = req.href.ticket(val) 
    294292                elif val is None: 
    295293                    val = '--' 
    296294                elif name in ('changetime', 'time'): 
     
    373371 
    374372    def get_sql(self, req=None, cached_ids=None): 
    375373        """Return a (sql, params) tuple for the query.""" 
    376         self.get_columns() 
     374        global is_op 
     375        if not self.cols: 
     376            self.get_columns() 
    377377 
    378378        enum_columns = ('resolution', 'priority', 'severity') 
    379379        # Build the list of actual columns to query 
     
    418418                       % (col, col, col)) 
    419419 
    420420        def get_constraint_sql(name, value, mode, neg): 
     421            # strip the operator from the value 
     422            value = value[len(mode) + is_op(neg):] 
     423            # convert the value for non string fields 
     424            current_field = {} 
     425            for field in self.fields  : 
     426                if field['name'] == name : 
     427                    current_field = field 
     428                    break 
     429            if current_field.has_key('type') \ 
     430                and current_field['type'] == 'date' : 
     431                    value = int(value)  
     432             
     433            # add the table alias to the field name 
    421434            if name not in custom_fields: 
    422435                name = 't.' + name 
    423436            else: 
    424437                name = name + '.value' 
    425             value = value[len(mode) + neg:] 
    426438 
    427439            if mode == '': 
    428                 return ("COALESCE(%s,'')%s=%%s" % (name, neg and '!' or ''), 
    429                         value) 
     440                return ("COALESCE(%s,'')%s=%%s" % (name,  
     441                                is_op(neg) and neg or ''), value) 
    430442            if not value: 
    431443                return None 
    432444            db = self.env.get_db_cnx() 
     
    437449                value = value + '%' 
    438450            elif mode == '$': 
    439451                value = '%' + value 
    440             return ("COALESCE(%s,'') %s%s" % (name, neg and 'NOT ' or '', 
     452            return ("COALESCE(%s,'') %s%s" % (name, neg == '!' and 'NOT ' or '', 
    441453                                              db.like()), 
    442454                    value) 
    443455 
     
    446458        for k, v in self.constraints.items(): 
    447459            if req: 
    448460                v = [val.replace('$USER', req.authname) for val in v] 
    449             # Determine the match mode of the constraint (contains, 
    450             # starts-with, negation, etc.) 
    451             neg = v[0].startswith('!') 
     461            # get the first character of the value, determine later if it's an 
     462            # operation 
     463            neg = '' 
    452464            mode = '' 
    453             if len(v[0]) > neg and v[0][neg] in ('~', '^', '$'): 
    454                 mode = v[0][neg] 
     465            if v[0] != '' : 
     466                neg = v[0][0] 
     467            mode = '' 
     468            if len(v[0]) > 1 and is_op(neg) and v[0][is_op(neg)] in ('~', '^', '$'): 
     469                    mode = v[0][is_op(neg)] 
    455470 
    456471            # Special case id ranges 
    457472            if k == 'id': 
    458473                ranges = Ranges() 
    459474                for r in v: 
    460                     r = r.replace('!', '') 
     475                    for ch in ['!', '<', '>'] : 
     476                        r = r.replace(ch, '') 
    461477                    ranges.appendrange(r) 
    462478                ids = [] 
    463479                id_clauses = [] 
     
    471487                if ids: 
    472488                    id_clauses.append('id IN (%s)' % (','.join(ids))) 
    473489                if id_clauses: 
    474                     clauses.append('%s(%s)' % (neg and 'NOT ' or '', 
     490                    clauses.append('%s(%s)' % (neg == '!' and 'NOT ' or '', 
    475491                                               ' OR '.join(id_clauses))) 
    476492            # Special case for exact matches on multiple values 
    477493            elif not mode and len(v) > 1: 
     
    480496                else: 
    481497                    col = k + '.value' 
    482498                clauses.append("COALESCE(%s,'') %sIN (%s)" 
    483                                % (col, neg and 'NOT ' or '', 
     499                               % (col, neg == '!' and 'NOT ' or '', 
    484500                                  ','.join(['%s' for val in v]))) 
    485                 args += [val[neg:] for val in v] 
     501                args += [val[is_op(neg):] for val in v] 
    486502            elif len(v) > 1: 
    487503                constraint_sql = filter(None, 
    488504                                        [get_constraint_sql(k, val, mode, neg) 
    489505                                         for val in v]) 
    490506                if not constraint_sql: 
    491507                    continue 
    492                 if neg: 
     508                if is_op(neg): 
    493509                    clauses.append("(" + " AND ".join( 
    494510                        [item[0] for item in constraint_sql]) + ")") 
    495511                else: 
     
    567583 
    568584    def template_data(self, context, tickets, orig_list=None, orig_time=None, 
    569585                      req=None): 
     586        global is_op 
    570587        constraints = {} 
    571588        for k, v in self.constraints.items(): 
    572589            constraint = {'values': [], 'mode': ''} 
    573590            for val in v: 
    574                 neg = val.startswith('!') 
    575                 if neg: 
     591                neg = '' 
     592                mode = '' 
     593                if val != '' : 
     594                    neg = val[0] 
     595                if val != '' and is_op(neg): 
    576596                    val = val[1:] 
    577597                mode = '' 
    578                 if val[:1] in ('~', '^', '$'): 
     598                if val != '' and val[:1] in ('~', '^', '$'): 
    579599                    mode, val = val[:1], val[1:] 
    580                 constraint['mode'] = (neg and '!' or '') + mode 
     600                constraint['mode'] = (is_op(neg) and neg or '') + mode 
    581601                constraint['values'].append(val) 
    582602            constraints[k] = constraint 
    583603 
    584604        cols = self.get_columns() 
    585605        labels = dict([(f['name'], f['label']) for f in self.fields]) 
    586606 
    587         # TODO: remove after adding time/changetime to the api.py 
    588         labels['changetime'] = _('Modified') 
    589         labels['time'] = _('Created') 
    590  
    591607        headers = [{ 
    592608            'name': col, 'label': labels.get(col, _('Ticket')), 
    593609            'href': self.get_href(context.href, order=col, 
     
    610626            {'name': _("begins with"), 'value': "^"}, 
    611627            {'name': _("ends with"), 'value': "$"}, 
    612628            {'name': _("is"), 'value': ""}, 
    613             {'name': _("is not"), 'value': "!"} 
     629            {'name': _("is not"), 'value': "!"}, 
     630            {'name': _("greater"), 'value': ">"}, 
     631            {'name': _("lesser"), 'value': "<"} 
    614632        ] 
    615633        modes['select'] = [ 
    616634            {'name': _("is"), 'value': ""}, 
     
    813831    def _get_constraints(self, req): 
    814832        constraints = {} 
    815833        ticket_fields = [f['name'] for f in 
    816                          TicketSystem(self.env).get_ticket_fields()] 
     834                         TicketSystem(self.env).get_all_ticket_fields()] 
    817835        ticket_fields.append('id') 
    818836 
    819837        # For clients without JavaScript, we remove constraints here if 
     
    10431061            kwargs['format'] = argv[0] 
    10441062 
    10451063        format = kwargs.pop('format', 'list').strip().lower() 
    1046         if format in ('list', 'compact'): # we need 'status' and 'summary' 
    1047             kwargs['col'] = '|'.join(['status', 'summary',  
    1048                                       kwargs.get('col', '')]) 
    10491064        query_string = '&'.join(['%s=%s' % item 
    10501065                                 for item in kwargs.iteritems()]) 
    10511066 
Note: See TracBrowser for help on using the repository browser.