source: peerreviewplugin/trunk/codereview/timeline.py

Last change on this file was 18283, checked in by Cinc-th, 2 years ago

PeerReviewPlugin: improve presentation of reviews in timeline. Respect timeline->changeset_show_files setting from trac.ini when rendering review files in timeline. Render user name instead of login name on timeline for reviewers and review creators.

File size: 6.0 KB
RevLine 
[17267]1# -*- coding: utf-8 -*-
[13497]2#
[18249]3# Copyright (C) 2016-2021 Cinc
[13497]4# All rights reserved.
5#
6# This software is licensed as described in the file COPYING.txt, which
7# you should have received as part of this distribution.
8#
[17267]9# Author: Cinc
10#
[18249]11from .model import PeerReviewModel, PeerReviewerModel, ReviewFileModel
[15172]12from trac.core import Component, implements
[15230]13from trac.wiki.formatter import format_to_html
[17267]14from trac.resource import Resource, get_resource_url
[13497]15from trac.timeline.api import ITimelineEventProvider
[17264]16from trac.util.datefmt import from_utimestamp, to_utimestamp
[16616]17from trac.util.html import html as tag
[18283]18from trac.util.text import to_unicode
[16616]19from trac.util.translation import _
[18283]20from trac.web.chrome import add_stylesheet, Chrome
[13497]21
[717]22
[15334]23class PeerReviewTimeline(Component):
[17265]24    """Provide code review events for the timeline.
25
26    [[BR]]
27    You need permission {{{CODE_REVIEW_VIEW}}} to see code review events.
28
29    '''Note:''' It is safe to disable this plugin when no timeline for code reviews should be shown.
30    """
31
[15334]32    implements(ITimelineEventProvider)
[15172]33
[3756]34    # ITimelineEventProvider methods
[15230]35
[3756]36    def get_timeline_filters(self, req):
[17264]37        if 'CODE_REVIEW_VIEW' in req.perm:
[15334]38            yield ('peerreview', _('Code Reviews'))
[3756]39
40    def get_timeline_events(self, req, start, stop, filters):
[15334]41        if 'peerreview' in filters:
[17267]42            ts_start = to_utimestamp(start)
43            ts_stop = to_utimestamp(stop)
[3756]44
[17267]45            coderev_resource = Resource('peerreview')
[18283]46            author_short = Chrome(self.env).authorinfo_short
[15334]47
[15230]48            add_stylesheet(req, 'hw/css/peerreview.css')
[3756]49
[17267]50            def reviewers_for_review(rev_id):
[15334]51                rm = PeerReviewerModel(self.env)
52                rm.clear_props()
[17267]53                rm['review_id'] = rev_id
[18283]54                reviewers = [author_short(reviewer['reviewer']) for reviewer in rm.list_matching_objects()]
55                return reviewers
[3756]56
[17267]57            def get_files_for_review_id(review_id):
58                """Get all files belonging to the given review id. Provide the number of comments if asked for."""
59                rfm = ReviewFileModel(self.env)
60                rfm.clear_props()
61                rfm['review_id'] = review_id
62                rev_files = list(rfm.list_matching_objects())
63                return rev_files
[3756]64
[17267]65            with self.env.db_query as db:
66                reviews = {}
67                for rid, t, author, field, oldvalue, newvalue \
68                        in db("""
69                                        SELECT pc.review_id, pc.time, pc.author,
70                                               pc.field, pc.oldvalue, pc.newvalue
71                                        FROM peerreview_change AS pc
72                                        WHERE pc.time>=%s AND pc.time<=%s
73                                        ORDER BY pc.time, pc.review_id
74                                        """, (ts_start, ts_stop)):
75                    if not (oldvalue or newvalue):
76                        # ignore empty change corresponding to custom field
77                        # created (None -> '') or deleted ('' -> None)
78                        continue
79                    if field == 'status':
80                        try:
81                            codereview, reviewers_list, coderev_page, files = reviews[rid]
82                        except KeyError:
83                            reviews[rid] = [PeerReviewModel(self.env, rid),
84                                            reviewers_for_review(rid),
85                                            coderev_resource(id=rid),
86                                            get_files_for_review_id(rid)
87                                            ]
88                            codereview, reviewers_list, coderev_page, files = reviews[rid]
89                        yield('peerreview', from_utimestamp(t), codereview['owner'],
90                              (coderev_page, codereview['name'], codereview['notes'],
91                               reviewers_list, oldvalue, newvalue, files))
92
93
[3756]94    def render_timeline_event(self, context, field, event):
[17267]95        codereview_page, name, notes, reviewersList, oldstatus, newstatus, files = event[3]
[18283]96        num_files = self.config.getint('timeline', 'changeset_show_files', 0)
[3756]97
98        if field == 'url':
[17267]99            return get_resource_url(self.env, codereview_page, context.href)
[3756]100        if field == 'title':
[17267]101            return tag(_('Code review '), tag.em(name), " (%s)" % codereview_page.id,
102                       _(": Status changed from '%s' to '%s'" % (oldstatus, newstatus))
103                       )
104
105        def filelist():
[18283]106            def create_li(f):
107                return tag.li(tag.div(),
[17267]108                                 tag.a('%s @ %s' % (f['path'], f['changerevision']),
[18282]109                                       href='peerreviewfile/%s' % f['file_id']
[17267]110                                       )
111                                )
[18283]112
113            ul = tag.ul(class_="rfiles")
114
115            if num_files == 0:
116                # don't show
117                return None
118            elif num_files == -1:
119                # unlimited
120                for rfile in files:
121                    ul.append(create_li(rfile))
122            else:
123                for rfile in files[:num_files]:
124                    ul.append(create_li(rfile))
125                if num_files < len(files):
126                    ul.append(tag.li(u'…'))
[17267]127            return ul
128
[3756]129        if field == 'description':
[18283]130            return tag(_('Assigned to: '),
131                       tag.ul([tag.li(reviewer) for reviewer in reviewersList],
132                              class_='userlist'),
133                       tag.div(_('Additional notes:')) if notes else None,
[15230]134                       tag.div(
135                           format_to_html(self.env, context, notes),
136                           class_='notes'
[18283]137                       ) if notes else None,
138                       tag.div(_('Files:')) if num_files else None,
[17267]139                       filelist()
140                       )
Note: See TracBrowser for help on using the repository browser.