source: peerreviewplugin/tags/0.12/3.1/codereview/peerReviewMain.py

Last change on this file was 17266, checked in by Cinc-th, 5 years ago

PeerReviewPlugin: record timestamp when finishing a review. The status considered as a terminal status are closed, approved, disapproved (unless configuration was changed). The finishing date is shown in the UI.

File size: 11.3 KB
Line 
1#
2# Copyright (C) 2005-2006 Team5
3# Copyright (C) 2016 Cinc-th
4#
5# All rights reserved.
6#
7# This software is licensed as described in the file COPYING.txt, which
8# you should have received as part of this distribution.
9#
10# Author: Team5
11#
12# Provides functionality for main page
13# Works with peerReviewMain.html
14
15import itertools
16from trac.core import Component, implements
17from trac.perm import IPermissionRequestor
18from trac.resource import IResourceManager, Resource, ResourceNotFound
19from trac.util import as_int, format_date
20from trac.util.html import Markup, html as tag
21from trac.util.translation import _
22from trac.web.chrome import INavigationContributor, ITemplateProvider, add_stylesheet, add_ctxtnav
23from trac.web.main import IRequestHandler
24from trac.wiki.formatter import format_to
25from model import ReviewCommentModel, ReviewDataModel, ReviewFileModel, PeerReviewModel, PeerReviewerModel
26from util import review_is_finished
27
28
29def web_context_compat(req, resource=None, id=False, version=False, parent=False,
30                absurls=False):
31    """Create a rendering context from a request.
32
33    The `perm` and `href` properties of the context will be initialized
34    from the corresponding properties of the request object.
35
36    >>> from trac.test import Mock, MockPerm
37    >>> req = Mock(href=Mock(), perm=MockPerm())
38    >>> context = web_context(req)
39    >>> context.href is req.href
40    True
41    >>> context.perm is req.perm
42    True
43
44    :param      req: the HTTP request object
45    :param resource: the `Resource` object or realm
46    :param       id: the resource identifier
47    :param  version: the resource version
48    :param  absurls: whether URLs generated by the ``href`` object should
49                     be absolute (including the protocol scheme and host
50                     name)
51    :return: a new rendering context
52    :rtype: `RenderingContext`
53
54    :since: version 1.0
55    """
56    from trac.mimeview.api import Context
57    if req:
58        href = req.abs_href if absurls else req.href
59        perm = req.perm
60    else:
61        href = None
62        perm = None
63    self = Context(Resource(resource, id=id, version=version,
64                            parent=parent), href=href, perm=perm)
65    self.req = req
66    return self
67
68try:
69    from trac.web.chrome import web_context
70except ImportError:
71    web_context = web_context_compat
72
73def add_ctxt_nav_items(req):
74    add_ctxtnav(req, _("My Code Reviews"), "peerReviewMain", title=_("My Code Reviews"))
75    add_ctxtnav(req, _("Create a Code Review"), "peerReviewNew", title=_("Create a Code review"))
76    add_ctxtnav(req, _("Report"), "peerreviewreport", title=_("Show Codereview Reports"))
77    add_ctxtnav(req, _("Search Code Reviews"), "peerReviewSearch", _("Search Code Reviews"))
78
79
80class PeerReviewMain(Component):
81    """Show overview page for code reviews."""
82    implements(INavigationContributor, IRequestHandler, ITemplateProvider,
83               IPermissionRequestor, IResourceManager)
84
85    # INavigationContributor methods
86
87    def get_active_navigation_item(self, req):
88        return 'peerReviewMain'
89
90    def get_navigation_items(self, req):
91        if 'CODE_REVIEW_DEV' in req.perm:
92            yield ('mainnav', 'peerReviewMain',
93                   Markup('<a href="%s">Peer Review</a>') % req.href.peerReviewMain())
94
95    # IPermissionRequestor methods
96
97    def get_permission_actions(self):
98        return [
99            ('CODE_REVIEW_VIEW',['PEERREVIEWFILE_VIEW', 'PEERREVIEW_VIEW']),  # Allow viewing of realm so reports show
100            ('CODE_REVIEW_DEV', ['CODE_REVIEW_VIEW']),                        # results.
101            ('CODE_REVIEW_MGR', ['CODE_REVIEW_DEV', 'PEERREVIEWFILE_VIEW'])
102        ]
103
104    # IRequestHandler methods
105
106    def send_preview(self, req):
107        # Taken from WikiRender component in wiki/web_api.py
108        # Allow all POST requests (with a valid __FORM_TOKEN, ensuring that
109        # the client has at least some permission). Additionally, allow GET
110        # requests from TRAC_ADMIN for testing purposes.
111        if req.method != 'POST':
112            req.perm.require('TRAC_ADMIN')
113        realm = req.args.get('realm', 'wiki')
114        id = req.args.get('id')
115        version = as_int(req.args.get('version'), None)
116        text = req.args.get('text', '')
117        flavor = req.args.get('flavor')
118        options = {}
119        if 'escape_newlines' in req.args:
120            options['escape_newlines'] = bool(int(req.args['escape_newlines']
121                                                  or 0))
122        if 'shorten' in req.args:
123            options['shorten'] = bool(int(req.args['shorten'] or 0))
124        resource = Resource(realm, id=id, version=version)
125        context = web_context(req, resource)
126        rendered = format_to(self.env, flavor, context, text, **options)
127        req.send(rendered.encode('utf-8'))
128
129
130    def match_request(self, req):
131        return req.path_info == '/peerReviewMain' or req.path_info == "/preview_render"
132
133    def process_request(self, req):
134        req.perm.require('CODE_REVIEW_DEV')
135
136        if req.path_info == "/preview_render":
137            self.send_preview(req)
138
139        data = {}
140        # test whether this user is a manager or not
141        if 'CODE_REVIEW_MGR' in req.perm:
142            data['manager'] = True
143        else:
144            data['manager'] = False
145
146        # User requests an update
147        data['allassigned'] = req.args.get('allassigned')
148        data['allcreated'] = req.args.get('allcreated')
149
150        r_tmpl = PeerReviewModel(self.env)
151        r_tmpl.clear_props()
152        if data['allcreated']:
153            all_reviews = list(r_tmpl.list_matching_objects())
154        else:
155            all_reviews = [rev for rev in r_tmpl.list_matching_objects() if rev['status'] != "closed"]
156
157        # We need this for displaying information about comments
158        comments = ReviewCommentModel.comments_by_file_id(self.env)
159        my_comment_data = ReviewDataModel.comments_for_owner(self.env, req.authname)
160
161        # Add files
162        files = ReviewFileModel.file_dict_by_review(self.env)
163
164        # fill the table of currently open reviews
165        myreviews = []
166        assigned_to_me =[]
167        manager_reviews = []
168
169        for rev in all_reviews:
170            # Reviews created by me
171            if rev['owner'] == req.authname:
172                rev.date = format_date(rev['created'])
173                if rev['closed']:
174                    rev.finish_date = format_date(rev['closed'])
175                else:
176                    rev.finish_date = ''
177                rev.rev_files = files[rev['review_id']]
178                # Prepare number of comments for a review
179                rev.num_comments = 0
180                for f in rev.rev_files:
181                    if f['file_id'] in comments:
182                        rev.num_comments += len(comments[f['file_id']])
183                rev.num_notread = rev.num_comments - len([c_id for c_id, r, t, dat in my_comment_data if t == 'read'
184                                                          and r == rev['review_id']])
185                myreviews.append(rev)
186
187        r_tmpl = PeerReviewerModel(self.env)
188        r_tmpl.clear_props()
189        r_tmpl['reviewer'] = req.authname
190
191        if data['allassigned']:
192            # Don't filter list here
193            reviewer = list(r_tmpl.list_matching_objects())
194        else:
195            reviewer = [rev for rev in r_tmpl.list_matching_objects() if rev['status'] != "reviewed"]
196
197        # All reviews assigned to me
198        for item in reviewer:
199            rev = PeerReviewModel(self.env, item['review_id'])
200            if not review_is_finished(self.env.config, rev):
201                rev.reviewer = item
202                rev.date = format_date(rev['created'])
203                if rev['closed']:
204                    rev.finish_date = format_date(rev['closed'])
205                else:
206                    rev.finish_date = ''
207                rev.rev_files = files[rev['review_id']]
208                # Prepare number of comments for a review
209                rev.num_comments = 0
210                for f in rev.rev_files:
211                    if f['file_id'] in comments:
212                        rev.num_comments += len(comments[f['file_id']])
213                rev.num_notread = rev.num_comments - len([c_id for c_id, r, t, dat in my_comment_data if t == 'read'
214                                                          and r == rev['review_id']])
215                assigned_to_me.append(rev)
216
217        data['myreviews'] = myreviews
218        data['manager_reviews'] = manager_reviews
219        data['assigned_reviews'] = assigned_to_me
220        data['cycle'] = itertools.cycle
221
222        add_stylesheet(req, 'hw/css/peerreview.css')
223        add_ctxt_nav_items(req)
224
225        return 'peerReviewMain.html', data, None
226
227    # IResourceManager methods
228
229    def get_resource_url(self, resource, href, **kwargs):
230        """Return the canonical URL for displaying the given resource.
231
232        :param resource: a `Resource`
233        :param href: an `Href` used for creating the URL
234
235        Note that if there's no special rule associated to this realm for
236        creating URLs (i.e. the standard convention of using realm/id applies),
237        then it's OK to not define this method.
238        """
239        if resource.realm == 'peerreviewfile':
240            return href('peerReviewPerform', IDFile=resource.id)
241        elif resource.realm == 'peerreview':
242            return href('peerReviewView', Review=resource.id)
243
244        return href('peerReviewMain')
245
246    def get_resource_realms(self):
247        yield 'peerreview'
248        yield 'peerreviewfile'
249
250    def get_resource_description(self, resource, format=None, context=None,
251                                 **kwargs):
252        desc = unicode(resource.id)
253        if resource.realm == 'peerreview':
254            if format == 'compact':
255                return 'review:%s' % resource.id  # Will be used as id in reports when 'realm' is used
256            else:
257                return 'Review %s' % resource.id
258        elif resource.realm == 'peerreviewfile':
259            if format == 'compact':
260                return 'rfile:%s' % resource.id
261            else:
262                return 'ReviewFile %s' % resource.id
263        return ""
264
265
266    def resource_exists(self, resource):
267        db = self.env.get_read_db()
268        cursor = db.cursor()
269        if resource.realm == 'peerreview':
270            cursor.execute("SELECT * FROM peerreview WHERE review_id = %s", (resource.id,))
271            if cursor.fetchone():
272                return True
273            else:
274                return False
275        elif resource.realm == 'peerreviewfile':
276            cursor.execute("SELECT * FROM peerreviewfile WHERE file_id = %s", (resource.id,))
277            if cursor.fetchone():
278                return True
279            else:
280                return False
281
282        raise ResourceNotFound('Resource %s not found.' % resource.realm)
283
284    # ITemplateProvider methods
285
286    def get_templates_dirs(self):
287        """Return the path of the directory containing the provided templates."""
288        from pkg_resources import resource_filename
289        return [resource_filename(__name__, 'templates')]
290
291    def get_htdocs_dirs(self):
292        from pkg_resources import resource_filename
293        return [('hw', resource_filename(__name__, 'htdocs'))]
Note: See TracBrowser for help on using the repository browser.