def _prepare_links(self, tkt): links = TicketLinks(self.env, tkt) links.blocking = set( int(n) for n in self.NUMBERS_RE.findall(tkt['blocking'] or '')) links.blocked_by = set( int(n) for n in self.NUMBERS_RE.findall(tkt['blockedby'] or '')) return links
def _depgraph_all(self, req): """ Produces a dependency graph including all tickets, even those which do not block other tickets and are not blocked by other tickets """ result = "" db = self.env.get_db_cnx() cursor = db.cursor() sql = "SELECT id, priority, summary FROM ticket WHERE status != 'closed' ORDER BY id DESC;" cursor.execute(sql) tickets = cursor.fetchall() for ticket in tickets: links = TicketLinks(self.env, int(ticket[0])) blockers = links.blocked_by if len(blockers) == 0 and len(links.blocking) == 0: # Orphan ticket, not blocked and not blocking, skip continue bgcolor, border = self._get_color(ticket[1]) result += "\"" + str(ticket[0]) + "\" [ URL=\"" \ + req.href.ticket(int(ticket[0])) \ + "\" fontcolor=\"#bb0000\" fillcolor=\"" + bgcolor \ + "\" color=\"" + border \ + "\" tooltip=\"" + ticket[2].replace('"', '"') \ + "\" ]\n" # Use blocked_by() from mastertickets.model.TicketLinks for blocker in blockers: # result += "\"%s\" -> \"%s\"" % (str(ticket[0]), str(blocker)) result += "\"%s\" -> \"%s\"" % (str(blocker), str(ticket[0])) return result
def post_process_request(self, req, template, data, content_type): from mastertickets.model import TicketLinks if req.path_info.startswith('/ticket'): ticket_id = req.path_info[8:] links = TicketLinks(self.env, ticket_id) if len(links.blocked_by) > 0: depgraph_href = req.href.depgraph(ticket_id) else: depgraph_href = None add_ctxtnav(req, "Dependency Graph", depgraph_href, "Dependency Graph") if req.path_info.startswith('/query'): query = {} percent_enc = re.compile('\%[0-9a-fA-F]') for line in data['query'].to_string().splitlines(): if '=' in line: if line.startswith('query:?'): line = line[7:] line = re.sub(percent_enc, self._unescape, line) key, value = line.split('=') if key in query: query[key].append(value) else: query[key] = [value] add_ctxtnav( req, tag.a('Dependency Graph', href=req.href('depgraph', **query))) return template, data, content_type
def validate_ticket(self, req, ticket): tid = ticket.id links = self._prepare_links(ticket) if req.args.get('action') == 'resolve' and \ req.args.get('action_resolve_resolve_resolution') == 'fixed': for i in links.blocked_by: if Ticket(self.env, i)['status'] != 'closed': yield None, "Ticket #%s is blocking this ticket" % i # Check that ticket does not have itself as a blocker if tid in links.blocking | links.blocked_by: yield 'blocked_by', "This ticket is blocking itself" return # Check that there aren't any blocked_by in blocking or their parents blocking = links.blocking.copy() while len(blocking) > 0: if len(links.blocked_by & blocking) > 0: yield 'blocked_by', "This ticket has circular dependencies" return new_blocking = set() for link in blocking: tmp_tkt = Ticket(self.env, link) new_blocking |= TicketLinks(self.env, tmp_tkt).blocking blocking = new_blocking for field in ('blocking', 'blockedby'): try: ids = self.NUMBERS_RE.findall(ticket[field] or '') for tid in ids[:]: for _ in self.env.db_query( """ SELECT id FROM ticket WHERE id=%s """, (tid, )): break else: ids.remove(tid) ticket[field] = ', '.join(sorted(ids, key=lambda x: int(x))) except Exception, e: self.log.debug("MasterTickets: Error parsing %s \"%s\": %s", field, ticket[field], e) yield field, "Not a valid list of ticket IDs"
def _depgraph(self, req, base, depth): try: ticket = int(base) except ValueError: if base.startswith('query:'): base = base[6:] elif base.startswith('report:'): req.perm.assert_permission('REPORT_VIEW') db = self.env.get_db_cnx() cursor = db.cursor() cursor.execute('SELECT query FROM report WHERE id=%s;', int(base[7:])) base = ''.join([line.strip() for line in cursor.splitlines()]) else: raise TracError('Unknown ticket identifier.') from trac.ticket.query import Query query = Query.from_string(self.env, base, max=0) return ''.join(self._depgraph(req, ticket['id'], -1) \ for ticket in query.execute(req)) self.log.debug('called depgraph(%s, %s)' % (str(ticket), str(depth))) if ticket in self._seen_tickets: return '' self._seen_tickets.append(ticket) links = TicketLinks(self.env, ticket) blockers = links.blocked_by if depth >= 0 and (len(blockers) == 0 and len(links.blocking) == 0): # Orphan ticket, not belonging to query, skip return '' db = self.env.get_db_cnx() cursor = db.cursor() sql = ("SELECT summary, priority FROM ticket WHERE id = %s;" \ % (str(ticket))) cursor.execute(sql) summary, priority = cursor.fetchone() if depth == 0: bgcolor = "#cceecc" border = "#00cc00" else: bgcolor, border = self._get_color(priority) depth = (depth > -1 and depth or 0) result = "\"" + str(ticket) + "\" [ URL=\"" \ + req.href.ticket(int(ticket)) \ + "\" fillcolor=\"" + bgcolor + "\" color=\"" + border \ + "\" fontcolor=\"#bb0000\" tooltip=\"" \ + summary.replace('"', '"') + "\" ]\n" if self._maxdepth > 0 and depth >= self._maxdepth: return result # Use blocked_by() from mastertickets.model.TicketLinks blockers = TicketLinks(self.env, ticket).blocked_by for blocker in blockers: result += self._depgraph(req, int(blocker), depth + 1) # result += "\"%s\" -> \"%s\"\n" % (str(ticket), str(blocker)) result += "\"%s\" -> \"%s\"\n" % (str(blocker), str(ticket)) return result
def ticket_deleted(self, tkt): links = TicketLinks(self.env, tkt) links.blocking = set() links.blocked_by = set() links.save('trac', "Ticket #%s deleted" % tkt.id)
def _prepare_links(self, tkt): links = TicketLinks(self.env, tkt) links.blocking = set(int(n) for n in self.NUMBERS_RE.findall(tkt['blocking'] or '')) links.blocked_by = set(int(n) for n in self.NUMBERS_RE.findall(tkt['blockedby'] or '')) return links