| 1 | # -*- coding: utf-8 -*- |
|---|
| 2 | # |
|---|
| 3 | # Copyright (C) 2016-2021 Cinc |
|---|
| 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 | # |
|---|
| 9 | # Author: Cinc |
|---|
| 10 | # |
|---|
| 11 | from trac.resource import Resource |
|---|
| 12 | from trac.wiki.formatter import format_to_oneliner |
|---|
| 13 | from trac.web.chrome import web_context |
|---|
| 14 | |
|---|
| 15 | from .model import PeerReviewModel, PeerReviewerModel, ReviewCommentModel, ReviewDataModel, ReviewFileModel |
|---|
| 16 | |
|---|
| 17 | |
|---|
| 18 | __author__ = 'Cinc' |
|---|
| 19 | __copyright__ = "Copyright 2016-2021" |
|---|
| 20 | __license__ = "BSD" |
|---|
| 21 | |
|---|
| 22 | |
|---|
| 23 | def to_db_path(path): |
|---|
| 24 | """Convert a path comming from Trac for db storage. |
|---|
| 25 | |
|---|
| 26 | A Trac path has no leading '/'. Files for reviews are stored with leading '/' |
|---|
| 27 | in the database. |
|---|
| 28 | """ |
|---|
| 29 | return '/' + path.lstrip('/') |
|---|
| 30 | |
|---|
| 31 | |
|---|
| 32 | def to_trac_path(path): |
|---|
| 33 | """Convert a file path from the review database for usage with Trac. |
|---|
| 34 | |
|---|
| 35 | A Trac path has no leading '/'. Files for reviews are stored with leading '/' |
|---|
| 36 | in the database. |
|---|
| 37 | """ |
|---|
| 38 | return path.lstrip('/') |
|---|
| 39 | |
|---|
| 40 | |
|---|
| 41 | def get_review_for_file(env, file_id): |
|---|
| 42 | rf = ReviewFileModel(env, file_id) |
|---|
| 43 | if not rf: |
|---|
| 44 | return None |
|---|
| 45 | rev = PeerReviewModel(env, rf['review_id']) |
|---|
| 46 | return rev |
|---|
| 47 | |
|---|
| 48 | |
|---|
| 49 | def not_allowed_to_comment(env, review, perm, authname): |
|---|
| 50 | """Check if the current user may comment on a file. |
|---|
| 51 | |
|---|
| 52 | For adding a comment you must either be: |
|---|
| 53 | |
|---|
| 54 | * the owner of the review |
|---|
| 55 | * one of the reviewers |
|---|
| 56 | * a user with permission CODE_REVIEW_MGR |
|---|
| 57 | |
|---|
| 58 | @return: True if commenting is not allowed, False otherwise |
|---|
| 59 | """ |
|---|
| 60 | # Don't let users comment who are not part of this review |
|---|
| 61 | reviewers = PeerReviewerModel.select_by_review_id(env, review['review_id']) |
|---|
| 62 | all_names = [reviewer['reviewer'] for reviewer in reviewers] |
|---|
| 63 | # Include owner of review in allowed names |
|---|
| 64 | all_names.append(review['owner']) # We don't care if the name is already in the list |
|---|
| 65 | |
|---|
| 66 | if authname not in all_names and 'CODE_REVIEW_MGR' not in perm: |
|---|
| 67 | return True |
|---|
| 68 | |
|---|
| 69 | return False |
|---|
| 70 | |
|---|
| 71 | |
|---|
| 72 | def review_is_finished(config, review): |
|---|
| 73 | """A finished review may only be reopened by a manager or admisnistrator |
|---|
| 74 | |
|---|
| 75 | :param config: Trac config object |
|---|
| 76 | :param review: review object |
|---|
| 77 | |
|---|
| 78 | :return True if review is in one of the terminal states |
|---|
| 79 | """ |
|---|
| 80 | finish_states = config.getlist("peerreview", "terminal_review_states") |
|---|
| 81 | return review['status'] in finish_states |
|---|
| 82 | |
|---|
| 83 | |
|---|
| 84 | def review_is_locked(config, review, authname=""): |
|---|
| 85 | """For a locked review a user can't change his voting |
|---|
| 86 | :param config: Trac config object |
|---|
| 87 | :param review: review object |
|---|
| 88 | :param authname: login name of user |
|---|
| 89 | |
|---|
| 90 | :return True if review is in lock state else False. Default lock state is |
|---|
| 91 | usually 'reviewed'. |
|---|
| 92 | |
|---|
| 93 | authname may be an empty string to check if a review is in the lock state at all. |
|---|
| 94 | If not empty the review is not locked for the user with the given login name. |
|---|
| 95 | """ |
|---|
| 96 | if review['owner'] == authname: |
|---|
| 97 | return False |
|---|
| 98 | |
|---|
| 99 | lock_states = config.getlist("peerreview", "reviewer_locked_states") |
|---|
| 100 | return review['status'] in lock_states |
|---|
| 101 | |
|---|
| 102 | |
|---|
| 103 | def get_changeset_html(env, req, rev, repos): |
|---|
| 104 | """Create html for use in templates from changeset and repository information |
|---|
| 105 | |
|---|
| 106 | :param env: Environment object |
|---|
| 107 | :param req: Request object |
|---|
| 108 | :param rev: changeset revision as a string |
|---|
| 109 | :param repos: Repository object for this changeset |
|---|
| 110 | |
|---|
| 111 | We use Tracs formatting functions to get proper Trac links with correct titles and rendering. |
|---|
| 112 | """ |
|---|
| 113 | if rev: |
|---|
| 114 | resource_repo = Resource('repository', repos.reponame) |
|---|
| 115 | changeset_html = format_to_oneliner(env, |
|---|
| 116 | web_context(req, |
|---|
| 117 | Resource('changeset', rev, parent=resource_repo)), |
|---|
| 118 | '[changeset:%s %s]' % (rev, rev)) |
|---|
| 119 | else: |
|---|
| 120 | changeset_html = '' |
|---|
| 121 | return changeset_html |
|---|
| 122 | |
|---|
| 123 | |
|---|
| 124 | def get_files_for_review_id(env, req, review_id, comments=False): |
|---|
| 125 | """Get all file objects belonging to the given review id. Provide the number of comments if asked for. |
|---|
| 126 | |
|---|
| 127 | :param env: Environment object |
|---|
| 128 | :param review_id: id of review as an int |
|---|
| 129 | :param req: Request object |
|---|
| 130 | :param comments: if True add information about comments as attributes to the file objects |
|---|
| 131 | :return: list of ReviewFileModels |
|---|
| 132 | """ |
|---|
| 133 | rev_files = list(ReviewFileModel.select_by_review(env, review_id)) |
|---|
| 134 | if comments: |
|---|
| 135 | for file_ in rev_files: |
|---|
| 136 | file_.comment_data = list(ReviewCommentModel.select_by_file_id(env, file_['file_id'])) |
|---|
| 137 | file_.num_comments = len(file_.comment_data) |
|---|
| 138 | my_comment_data = ReviewDataModel.comments_for_file_and_owner(env, file_['file_id'], req.authname) |
|---|
| 139 | file_.num_notread = file_.num_comments - len([c_id for c_id, t, dat in my_comment_data if t == 'read']) |
|---|
| 140 | return rev_files |
|---|