Esempio n. 1
0
 def _build_graph(self, req, tkt_id):
     links = TicketLinks(self.env, tkt_id)
     
     g = graphviz.Graph()
     
     node_default = g['node']
     node_default['style'] = 'filled'
     
     edge_default = g['edge']
     edge_default['style'] = ''
     
     # Force this to the top of the graph
     g[tkt_id] 
     
     links = sorted(links.walk(), key=lambda link: link.tkt.id)
     for link in links:
         tkt = link.tkt
         node = g[tkt.id]
         node['label'] = u'#%s'%tkt.id
         node['fillcolor'] = tkt['status'] == 'closed' and 'green' or 'red'
         node['URL'] = req.href.ticket(tkt.id)
         node['alt'] = u'Ticket #%s'%tkt.id
         node['tooltip'] = tkt['summary']
         
         for n in link.blocking:
             node > g[n]
     
     return g
Esempio n. 2
0
    def _build_graph(self, req, tkt_id):
        links = TicketLinks(self.env, tkt_id)

        g = graphviz.Graph()

        node_default = g['node']
        node_default['style'] = 'filled'

        edge_default = g['edge']
        edge_default['style'] = ''

        # Force this to the top of the graph
        g[tkt_id]

        links = sorted(links.walk(), key=lambda link: link.tkt.id)
        for link in links:
            tkt = link.tkt
            node = g[tkt.id]
            node['label'] = u'#%s' % tkt.id
            node['fillcolor'] = tkt['status'] == 'closed' and 'green' or 'red'
            node['URL'] = req.href.ticket(tkt.id)
            node['alt'] = u'Ticket #%s' % tkt.id
            node['tooltip'] = tkt['summary']

            for n in link.blocking:
                node > g[n]

        return g
Esempio n. 3
0
 def _prepare_links(self, tkt, db):
     links = TicketLinks(self.env, tkt, db)
     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
Esempio n. 4
0
 def ticket_deleted(self, ticket):
     if self.has_ticket_refs(ticket):
         self.log.debug("TicketRefsPlugin: ticket are deleting")
         links = TicketLinks(self.env, ticket)
         try:
             links.delete()
         except Exception, err:
             self.log.error("TicketRefsPlugin: ticket_deleted %s" % err)
         self.log.debug("TicketRefsPlugin: ticket are deleted")
Esempio n. 5
0
    def ticket_deleted(self, tkt):
        db = self.env.get_db_cnx()

        links = TicketLinks(self.env, tkt, db)
        links.blocking = set()
        links.blocked_by = set()
        links.save('trac', 'Ticket #%s deleted' % tkt.id, when=None, db=db)

        db.commit()
Esempio n. 6
0
 def ticket_changed(self, tkt, comment, author, old_values):
     db = self.env.get_db_cnx()
     
     links = TicketLinks(self.env, tkt, db)
     links.blocking = set(self.NUMBERS_RE.findall(tkt['blocking'] or ''))
     links.blocked_by = set(self.NUMBERS_RE.findall(tkt['blockedby'] or ''))
     links.save(author, comment, tkt.time_changed, db)
     
     db.commit()
Esempio n. 7
0
    def ticket_created(self, ticket):
        links = None

        if self.has_ticket_refs(ticket):
            self.log.debug("TicketRefsPlugin: ticket are creating")
            if not links:
                links = TicketLinks(self.env, ticket)
            try:
                links.create()
            except Exception, err:
                self.log.error("TicketRefsPlugin: ticket_created %s" % err)
            self.log.debug("TicketRefsPlugin: ticket are created")
Esempio n. 8
0
    def ticket_changed(self, tkt, comment, author, old_values):
        db = self.env.get_db_cnx()

        links = TicketLinks(self.env, tkt, db)

        old_relations = {'blocking': set([]), 'blockedby': set([])}
        if "blocking" in old_values:
            old_relations['blocking'] = extract_ticket_ids(old_values['blocking'])

        if "blockedby" in old_values:
            old_relations['blockedby'] = extract_ticket_ids(old_values['blockedby'])

        links.save(old_relations, author, comment, tkt.time_changed, db)

        db.commit()
Esempio n. 9
0
 def validate_ticket(self, req, ticket):
     if req.args.get('action') == 'resolve' and req.args.get(
             'action_resolve_resolve_resolution') == 'fixed':
         links = TicketLinks(self.env, ticket)
         for i in links.blocked_by:
             if Ticket(self.env, i)['status'] != 'closed':
                 yield None, 'Ticket #%s is blocking this ticket' % i
    def _build_graph(self, req, tkt_ids, label_summary=0):
        g = graphviz.Graph()
        g.label_summary = label_summary

        g.attributes['rankdir'] = self.graph_direction
        
        node_default = g['node']
        node_default['style'] = 'filled'
        
        edge_default = g['edge']
        edge_default['style'] = ''
        
        # Force this to the top of the graph
        for id in tkt_ids:
            g[id] 
        
        links = TicketLinks.walk_tickets(self.env, tkt_ids)
        links = sorted(links, key=lambda link: link.tkt.id)
        for link in links:
            tkt = link.tkt
            node = g[tkt.id]
            if label_summary:
                node['label'] = u'#%s %s' % (tkt.id, tkt['summary'])
            else:
                node['label'] = u'#%s'%tkt.id
            node['fillcolor'] = tkt['status'] == 'closed' and self.closed_color or self.opened_color
            node['URL'] = req.href.ticket(tkt.id)
            node['alt'] = u'Ticket #%s'%tkt.id
            node['tooltip'] = tkt['summary']
            
            for n in link.blocking:
                node > g[n]
        
        return g
Esempio n. 11
0
    def _build_graph(self, req, tkt_ids, label_summary=0):
        g = graphviz.Graph()
        g.label_summary = label_summary

        g.attributes['rankdir'] = self.graph_direction

        node_default = g['node']
        node_default['style'] = 'filled'

        edge_default = g['edge']
        edge_default['style'] = ''

        # Force this to the top of the graph
        for id in tkt_ids:
            g[id]

        links = TicketLinks.walk_tickets(self.env, tkt_ids)
        links = sorted(links, key=lambda link: link.tkt.id)
        for link in links:
            tkt = link.tkt
            node = g[tkt.id]
            if label_summary:
                node['label'] = u'#%s %s' % (tkt.id, tkt['summary'])
            else:
                node['label'] = u'#%s' % tkt.id
            node['fillcolor'] = tkt[
                'status'] == 'closed' and self.closed_color or self.opened_color
            node['URL'] = req.href.ticket(tkt.id)
            node['alt'] = u'Ticket #%s' % tkt.id
            node['tooltip'] = tkt['summary']

            for n in link.blocking:
                node > g[n]

        return g
Esempio n. 12
0
    def post_process_request(self, req, template, data, content_type):
        if req.path_info.startswith('/ticket/'):
            # In case of an invalid ticket, the data is invalid
            if not data:
                return template, data, content_type
            tkt = data['ticket']
            links = TicketLinks(self.env, tkt)

            for i in links.blocked_by:
                if Ticket(self.env, i)['status'] != 'closed':
                    add_script(req, 'mastertickets/disable_resolve.js')
                    break

            # Add link to depgraph if needed
            if links:
                add_ctxtnav(req, 'Depgraph',
                            req.href.depgraph('ticket', tkt.id))

            for change in data.get('changes', {}):
                if not change.has_key('fields'):
                    continue
                for field, field_data in change['fields'].iteritems():
                    if field in self.fields:
                        if field_data['new'].strip():
                            new = set(
                                [int(n) for n in field_data['new'].split(',')])
                        else:
                            new = set()
                        if field_data['old'].strip():
                            old = set(
                                [int(n) for n in field_data['old'].split(',')])
                        else:
                            old = set()
                        add = new - old
                        sub = old - new
                        elms = tag()
                        if add:
                            elms.append(
                                tag.em(u', '.join(
                                    [unicode(n) for n in sorted(add)])))
                            elms.append(u' added')
                        if add and sub:
                            elms.append(u'; ')
                        if sub:
                            elms.append(
                                tag.em(u', '.join(
                                    [unicode(n) for n in sorted(sub)])))
                            elms.append(u' removed')
                        field_data['rendered'] = elms

        #add a link to generate a dependency graph for all the tickets in the milestone
        if req.path_info.startswith('/milestone/'):
            if not data:
                return template, data, content_type
            milestone = data['milestone']
            add_ctxtnav(req, 'Depgraph',
                        req.href.depgraph('milestone', milestone.name))

        return template, data, content_type
Esempio n. 13
0
    def post_process_request(self, req, template, data, content_type):
        if req.path_info.startswith('/ticket/'):
            tkt = data['ticket']
            links = TicketLinks(self.env, tkt)

            for i in links.blocked_by:
                if Ticket(self.env, i)['status'] != 'closed':
                    add_script(req, 'mastertickets/disable_resolve.js')
                    break

            data['mastertickets'] = {
                'field_values': {
                    'blocking': linkify_ids(self.env, req, links.blocking),
                    'blockedby': linkify_ids(self.env, req, links.blocked_by),
                },
            }

            # Add link to depgraph if needed
            if links:
                add_ctxtnav(req, 'Depgraph', req.href.depgraph(tkt.id))

            for change in data.get('changes', []):
                for field, field_data in change['fields'].iteritems():
                    if field in self.fields:
                        if field_data['new'].strip():
                            new = set(
                                [int(n) for n in field_data['new'].split(',')])
                        else:
                            new = set()
                        if field_data['old'].strip():
                            old = set(
                                [int(n) for n in field_data['old'].split(',')])
                        else:
                            old = set()
                        add = new - old
                        sub = old - new
                        elms = tag()
                        if add:
                            elms.append(
                                tag.em(u', '.join(
                                    [unicode(n) for n in sorted(add)])))
                            elms.append(u' added')
                        if add and sub:
                            elms.append(u'; ')
                        if sub:
                            elms.append(
                                tag.em(u', '.join(
                                    [unicode(n) for n in sorted(sub)])))
                            elms.append(u' removed')
                        field_data['rendered'] = elms

        return template, data, content_type
Esempio n. 14
0
    def ticket_changed(self, ticket, comment, author, old_values):
        links = None
        need_change = TICKETREF in old_values

        #com_refs = get_refs_in_comment(comment, [ticket.id])
        #if com_refs:
        #    self.log.debug("TicketRefsPlugin: refs in comment %s" % (
        #                   str(com_refs)))
        #    links = TicketLinks(self.env, ticket)
        #    links.add_reference(com_refs)
        #    need_change = True

        if need_change:
            self.log.debug("TicketRefsPlugin: ticket are changing")
            if not links:
                links = TicketLinks(self.env, ticket)
            try:
                print "will call ticketLinks.change"
                links.change(author, old_values.get(TICKETREF))
            except Exception, err:
                self.log.error("TicketRefsPlugin: ticket_changed %s" % err)
            self.log.debug("TicketRefsPlugin: ticket are changed")            
Esempio n. 15
0
    def validate_ticket(self, req, ticket):
        db = self.env.get_db_cnx()
        cursor = db.cursor()
        
        id = unicode(ticket.id)
        links = TicketLinks(self.env, ticket, db)
        links.blocking = extract_ticket_ids(ticket['blocking'] or '')
        links.blocked_by = extract_ticket_ids(ticket['blockedby'] or '')
        
        # Check that ticket does not have itself as a blocker 
        if id 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, db).blocking
            blocking = new_blocking
        
        for field in ('blocking', 'blockedby'):
            try:
                ids = self.NUMBERS_RE.findall(ticket[field] or '')
                for id in ids[:]:
                    cursor.execute('SELECT id FROM ticket WHERE id=%s', (id,))
                    row = cursor.fetchone()
                    if row is None:
                        ids.remove(id)
                ticket[field] = ', '.join(sorted(ids, key=lambda x: int(x)))
            except Exception, e:
                self.log.debug('TicketRelations: Error parsing %s "%s": %s', field, ticket[field], e)
                yield field, 'Not a valid list of ticket IDs'
Esempio n. 16
0
 def ticket_deleted(self, tkt):
     db = self.env.get_db_cnx()
     
     links = TicketLinks(self.env, tkt, db)
     links.blocking = set()
     links.blocked_by = set()
     links.save('trac', 'Ticket #%s deleted'%tkt.id, when=None, db=db)
     
     db.commit()
Esempio n. 17
0
    def ticket_changed(self, tkt, comment, author, old_values):
        db = self.env.get_db_cnx()

        links = TicketLinks(self.env, tkt, db)
        links.blocking = set(self.NUMBERS_RE.findall(tkt['blocking'] or ''))
        links.blocked_by = set(self.NUMBERS_RE.findall(tkt['blockedby'] or ''))
        links.save(author, comment, tkt.time_changed, db)

        db.commit()
Esempio n. 18
0
    def process_request(self, req):
        path_info = req.path_info[10:]

        if not path_info:
            raise TracError('No ticket specified')

        tkt_id = path_info.split('/', 1)[0]
        g = self._build_graph(req, tkt_id)
        if '/' in path_info or 'format' in req.args:

            format = req.args.get('format')
            if format == 'text':
                req.send(str(g), 'text/plain')
            elif format == 'debug':
                import pprint
                req.send(pprint.pformat(TicketLinks(self.env, tkt_id)),
                         'text/plain')
            elif format is not None:
                req.send(g.render(self.dot_path, format), 'text/plain')

            if self.use_gs:
                ps = g.render(self.dot_path, 'ps2')
                gs = subprocess.Popen([
                    self.gs_path, '-q', '-dTextAlphaBits=4',
                    '-dGraphicsAlphaBits=4', '-sDEVICE=png16m', '-o',
                    '%stdout%', '-'
                ],
                                      stdin=subprocess.PIPE,
                                      stdout=subprocess.PIPE,
                                      stderr=subprocess.PIPE)
                img, err = gs.communicate(ps)
                if err:
                    self.log.debug('MasterTickets: Error from gs: %s', err)
            else:
                img = g.render(self.dot_path)
            req.send(img, 'image/png')
        else:
            data = {}

            tkt = Ticket(self.env, tkt_id)
            data['tkt'] = tkt
            data['graph'] = g
            data['graph_render'] = partial(g.render, self.dot_path)
            data['use_gs'] = self.use_gs

            add_ctxtnav(req, 'Back to Ticket #%s' % tkt.id,
                        req.href.ticket(tkt_id))
            return 'depgraph.html', data, None
Esempio n. 19
0
    def _build_graph(self, req, tkt_ids, label_summary=0):
        g = graphviz.Graph(log=self.log)
        g.label_summary = label_summary

        g.attributes['rankdir'] = self.graph_direction

        node_default = g['node']
        node_default['style'] = 'filled'

        edge_default = g['edge']
        edge_default['style'] = ''

        # Force this to the top of the graph
        for tid in tkt_ids:
            g[tid]

        if self.show_key:
            g[-1]['label'] = self.closed_text
            g[-1]['fillcolor'] = self.closed_color
            g[-1]['shape'] = 'box'
            g[-2]['label'] = self.opened_text
            g[-2]['fillcolor'] = self.opened_color
            g[-2]['shape'] = 'box'

        links = TicketLinks.walk_tickets(self.env, tkt_ids, self.full_graph)
        links = sorted(links, key=lambda link: link.tkt.id)
        for link in links:
            tkt = link.tkt
            node = g[tkt.id]
            if label_summary:
                node['label'] = u'#%s %s' % (tkt.id, tkt['summary'])
            else:
                node['label'] = u'#%s' % tkt.id
            node['fillcolor'] = tkt['status'] == 'closed' and \
                                self.closed_color or self.opened_color
            node['URL'] = req.href.ticket(tkt.id)
            node['alt'] = u'Ticket #%s' % tkt.id
            node['tooltip'] = escape(tkt['summary'])
            if self.highlight_target and tkt.id in tkt_ids:
                node['penwidth'] = 3

            for n in link.blocking:
                node > g[n]

        return g
    def _build_graph(self, req, tkt_ids, label_summary=0):
        g = graphviz.Graph(log=self.log)
        g.label_summary = label_summary

        g.attributes['rankdir'] = self.graph_direction

        node_default = g['node']
        node_default['style'] = 'filled'

        edge_default = g['edge']
        edge_default['style'] = ''

        # Force this to the top of the graph
        for id in tkt_ids:
            g[id]

        if self.show_key:
            g[-1]['label'] = self.closed_text
            g[-1]['fillcolor'] = self.closed_color
            g[-1]['shape'] = 'box'
            g[-2]['label'] = self.opened_text
            g[-2]['fillcolor'] = self.opened_color
            g[-2]['shape'] = 'box'

        links = TicketLinks.walk_tickets(self.env, tkt_ids,
                                         full=self.full_graph)
        links = sorted(links, key=lambda link: link.tkt.id)
        for link in links:
            tkt = link.tkt
            node = g[tkt.id]
            if label_summary:
                node['label'] = u'#%s %s' % (tkt.id, tkt['summary'])
            else:
                node['label'] = u'#%s' % tkt.id
            node['fillcolor'] = tkt['status'] == 'closed' and self.closed_color or self.opened_color
            node['URL'] = req.href.ticket(tkt.id)
            node['alt'] = u'Ticket #%s' % tkt.id
            node['tooltip'] = escape(tkt['summary'])
            if self.highlight_target and tkt.id in tkt_ids:
                node['penwidth'] = 3

            for n in link.blocking:
                node > g[n]

        return g
Esempio n. 21
0
    def validate_ticket(self, req, ticket):
        db = self.env.get_db_cnx()
        cursor = db.cursor()

        tid = ticket.id
        links = self._prepare_links(ticket, db)

        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, db).blocking
            blocking = new_blocking

        for field in ('blocking', 'blockedby'):
            try:
                ids = self.NUMBERS_RE.findall(ticket[field] or '')
                for tid in ids[:]:
                    cursor.execute('SELECT id FROM ticket WHERE id=%s',
                                   (tid, ))
                    row = cursor.fetchone()
                    if row is None:
                        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'
Esempio n. 22
0
 def _prepare_links(self, tkt, db):
     links = TicketLinks(self.env, tkt, db)
     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
Esempio n. 23
0
    def process_request(self, req):
        realm = req.args['realm']
        id_ = req.args['id']

        if not which(self.dot_path):
            raise TracError(
                _("Path to dot executable is invalid: %(path)s",
                  path=self.dot_path))

        # Urls to generate the depgraph for a ticket is /depgraph/ticketnum
        # Urls to generate the depgraph for a milestone is
        # /depgraph/milestone/milestone_name

        # List of tickets to generate the depgraph.
        if realm == 'milestone':
            # We need to query the list of tickets in the milestone
            query = Query(self.env, constraints={'milestone': [id_]}, max=0)
            tkt_ids = [fields['id'] for fields in query.execute(req)]
        else:
            tid = as_int(id_, None)
            if tid is None:
                raise TracError(
                    tag_("%(id)s is not a valid ticket id.", id=html.tt(id_)))
            tkt_ids = [tid]

        # The summary argument defines whether we place the ticket id or
        # its summary in the node's label
        label_summary = 0
        if 'summary' in req.args:
            label_summary = int(req.args.get('summary'))

        g = self._build_graph(req, tkt_ids, label_summary=label_summary)
        if req.path_info.endswith('/depgraph.png') or 'format' in req.args:
            format_ = req.args.get('format')
            if format_ == 'text':
                # In case g.__str__ returns unicode, convert it in ascii
                req.send(
                    to_unicode(g).encode('ascii', 'replace'), 'text/plain')
            elif format_ == 'debug':
                import pprint

                req.send(
                    pprint.pformat(
                        [TicketLinks(self.env, tkt_id) for tkt_id in tkt_ids]),
                    'text/plain')
            elif format_ is not None:
                if format_ in self.acceptable_formats:
                    req.send(g.render(self.dot_path, format_), 'text/plain')
                else:
                    raise TracError(
                        _("The %(format)s format is not allowed.",
                          format=format_))

            if self.use_gs:
                ps = g.render(self.dot_path, 'ps2')
                gs = subprocess.Popen([
                    self.gs_path, '-q', '-dTextAlphaBits=4',
                    '-dGraphicsAlphaBits=4', '-sDEVICE=png16m',
                    '-sOutputFile=%stdout%', '-'
                ],
                                      stdin=subprocess.PIPE,
                                      stdout=subprocess.PIPE,
                                      stderr=subprocess.PIPE)
                img, err = gs.communicate(ps)
                if err:
                    self.log.debug('MasterTickets: Error from gs: %s', err)
            else:
                img = g.render(self.dot_path)
            req.send(img, 'image/png')
        else:
            data = {}

            # Add a context link to enable/disable labels in nodes.
            if label_summary:
                add_ctxtnav(req, 'Without labels',
                            req.href(req.path_info, summary=0))
            else:
                add_ctxtnav(req, 'With labels',
                            req.href(req.path_info, summary=1))

            if realm == 'milestone':
                add_ctxtnav(req, 'Back to Milestone: %s' % id_,
                            req.href.milestone(id_))
                data['milestone'] = id_
            else:
                data['ticket'] = id_
                add_ctxtnav(req, 'Back to Ticket #%s' % id_,
                            req.href.ticket(id_))
            data['graph'] = g
            data['graph_render'] = functools.partial(g.render, self.dot_path)
            data['use_gs'] = self.use_gs

            return 'depgraph.html', data, None
Esempio n. 24
0
    def _build_graph(self, req, tkt_ids, label_summary=0, with_clusters=False):
        g = graphviz.Graph()
        g.label_summary = label_summary

        g.attributes.update({
            'rankdir': self.graph_direction,
        })

        node_default = g['node']
        node_default.update({
            'style': 'filled',
            'fontsize': 11,
            'fontname': 'Arial',
            'shape': 'box' if label_summary else 'ellipse',
            'target': '_blank',
        })

        edge_default = g['edge']
        edge_default['style'] = ''

        width = 20
        def q(text):
            return textwrap.fill(text, width).replace('"', '\\"').replace('\n', '\\n')

        def create_node(tkt):
            node = g.get_node(tkt.id)
            summary = q(tkt['summary'])
            if label_summary:
                node['label'] = u'#%s %s' % (tkt.id, summary)
            else:
                node['label'] = u'#%s'%tkt.id
            if tkt['status'] == 'closed':
                color = tkt['resolution'] in bc_resolutions and self.bad_closed_color or self.closed_color
            else:
                color = self.opened_color
            node['fillcolor'] = color
            node['URL'] = req.href.ticket(tkt.id)
            node['alt'] = _('Ticket #%(id)s', id=tkt.id)
            node['tooltip'] = summary.replace('\\n', ' 
')
            return node

        ticket_cache = {}

        if with_clusters:
            milestone_tkt_ids = sorted(tkt_ids)
            tkt_ids = []
            tickets = {} # { <milestone_name or None>: (<cluster or graph>, <tkt_ids list>), ... }
            ticket_milestones = {} # { <tkt_id>: <milestone> }
            m_idx = 0
            for milestone, mtkt_ids in itertools.groupby(milestone_tkt_ids, lambda p: p[0]):
                ids = [p[1] for p in mtkt_ids]
                if milestone:
                    m_idx += 1
                    url = req.href.depgraph(get_resource_url(self.env,
                                            Resource('milestone', milestone, pid=req.data['project_id'])))
                    tickets[milestone] = (g.create_cluster(
                            u'cluster%s' % m_idx,
                            label=q(milestone),
                            href=url, target='_blank'),
                                          ids)
                else:
                    milestone = None
                    tickets[None] = (g, ids)
                tkt_ids.extend(ids)
                for tkt_id in ids:
                    ticket_milestones[tkt_id] = milestone
        else:
            # Init nodes for resource tickets on graph top
            for id in tkt_ids:
                g[id]

        bc_resolutions = self.bad_closed_resolutions.syllabus(req.data['syllabus_id'])
        links = TicketLinks.walk_tickets(self.env, tkt_ids, ticket_cache)
        links = sorted(links, key=lambda link: link.tkt.id)
        for link in links:
            node = create_node(link.tkt)

            if with_clusters:
                milestone_from = link.tkt['milestone'] or None
                stor = tickets[milestone_from][0]
                stor[link.tkt.id] # include node
                for n in link.blocking:
                    milestone_to = ticket_milestones[n]
                    if milestone_from == milestone_to:
                        stor.add(node > stor[n]) # save edge in same cluster
                    else:
                        g.add(node > tickets[milestone_to][0][n]) # save edge in global graph
            else:
                g[link.tkt.id]
                for n in link.blocking:
                    g.add(node > g[n])

        return g
Esempio n. 25
0
    def process_request(self, req):
        path_info = req.path_info[10:]

        if not path_info:
            raise TracError('No ticket specified')

        #list of tickets to generate the depgraph for
        tkt_ids = []
        milestone = None
        split_path = path_info.split('/', 2)

        #Urls to generate the depgraph for a ticket is /depgraph/ticketnum
        #Urls to generate the depgraph for a milestone is /depgraph/milestone/milestone_name
        if split_path[0] == 'milestone':
            #we need to query the list of tickets in the milestone
            milestone = split_path[1]
            query = Query(self.env,
                          constraints={'milestone': [milestone]},
                          max=0)
            tkt_ids = [fields['id'] for fields in query.execute()]
        else:
            #the list is a single ticket
            tkt_ids = [int(split_path[0])]

        #the summary argument defines whether we place the ticket id or
        #it's summary in the node's label
        label_summary = 0
        if 'summary' in req.args:
            label_summary = int(req.args.get('summary'))

        g = self._build_graph(req, tkt_ids, label_summary=label_summary)
        if path_info.endswith('/depgraph.png') or 'format' in req.args:
            format = req.args.get('format')
            if format == 'text':
                #in case g.__str__ returns unicode, we need to convert it in ascii
                req.send(
                    to_unicode(g).encode('ascii', 'replace'), 'text/plain')
            elif format == 'debug':
                import pprint
                req.send(
                    pprint.pformat(
                        [TicketLinks(self.env, tkt_id) for tkt_id in tkt_ids]),
                    'text/plain')
            elif format is not None:
                req.send(g.render(self.dot_path, format), 'text/plain')

            if self.use_gs:
                ps = g.render(self.dot_path, 'ps2')
                gs = subprocess.Popen([
                    self.gs_path, '-q', '-dTextAlphaBits=4',
                    '-dGraphicsAlphaBits=4', '-sDEVICE=png16m',
                    '-sOutputFile=%stdout%', '-'
                ],
                                      stdin=subprocess.PIPE,
                                      stdout=subprocess.PIPE,
                                      stderr=subprocess.PIPE)
                img, err = gs.communicate(ps)
                if err:
                    self.log.debug('MasterTickets: Error from gs: %s', err)
            else:
                img = g.render(self.dot_path)
            req.send(img, 'image/png')
        else:
            data = {}

            #add a context link to enable/disable labels in nodes
            if label_summary:
                add_ctxtnav(req, 'Without labels',
                            req.href(req.path_info, summary=0))
            else:
                add_ctxtnav(req, 'With labels',
                            req.href(req.path_info, summary=1))

            if milestone is None:
                tkt = Ticket(self.env, tkt_ids[0])
                data['tkt'] = tkt
                add_ctxtnav(req, 'Back to Ticket #%s' % tkt.id,
                            req.href.ticket(tkt.id))
            else:
                add_ctxtnav(req, 'Back to Milestone %s' % milestone,
                            req.href.milestone(milestone))
            data['milestone'] = milestone
            data['graph'] = g
            data['graph_render'] = partial(g.render, self.dot_path)
            data['use_gs'] = self.use_gs

            return 'depgraph.html', data, None