~launchpad-pqm/launchpad/devel

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
# Copyright 2009 Canonical Ltd.  This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).

"""CodeReviewVoteReference database class."""

__metaclass__ = type
__all__ = [
    'CodeReviewVoteReference',
    ]

from sqlobject import (
    ForeignKey,
    StringCol,
    )
from zope.interface import implements
from zope.schema import Int

from lp.code.errors import (
    ClaimReviewFailed,
    ReviewNotPending,
    UserHasExistingReview,
    )
from lp.code.interfaces.codereviewvote import ICodeReviewVoteReference
from lp.services.database.constants import DEFAULT
from lp.services.database.datetimecol import UtcDateTimeCol
from lp.services.database.sqlbase import SQLBase


class CodeReviewVoteReference(SQLBase):
    """See `ICodeReviewVote`"""

    implements(ICodeReviewVoteReference)

    _table = 'CodeReviewVote'
    id = Int()
    branch_merge_proposal = ForeignKey(
        dbName='branch_merge_proposal', foreignKey='BranchMergeProposal',
        notNull=True)
    date_created = UtcDateTimeCol(notNull=True, default=DEFAULT)
    registrant = ForeignKey(
        dbName='registrant', foreignKey='Person', notNull=True)
    reviewer = ForeignKey(
        dbName='reviewer', foreignKey='Person', notNull=True)
    review_type = StringCol(default=None)
    comment = ForeignKey(
        dbName='vote_message', foreignKey='CodeReviewComment', default=None)

    @property
    def is_pending(self):
        """See `ICodeReviewVote`"""
        # Reviews are pending if there is no associated comment.
        return self.comment is None

    def _validatePending(self):
        """Raise if the review is not pending."""
        if not self.is_pending:
            raise ReviewNotPending('The review is not pending.')

    def _validateNoReviewForUser(self, user):
        """Make sure there isn't an existing review for the user."""
        bmp = self.branch_merge_proposal
        existing_review = bmp.getUsersVoteReference(user)
        if existing_review is not None:
            if existing_review.is_pending:
                error_str = '%s has already been asked to review this'
            else:
                error_str = '%s has already reviewed this'
            raise UserHasExistingReview(error_str % user.unique_displayname)

    def validateClaimReview(self, claimant):
        """See `ICodeReviewVote`"""
        self._validatePending()
        if not self.reviewer.is_team:
            raise ClaimReviewFailed('Cannot claim non-team reviews.')
        if not claimant.inTeam(self.reviewer):
            raise ClaimReviewFailed(
                '%s is not a member of %s' %
                (claimant.unique_displayname,
                 self.reviewer.unique_displayname))
        self._validateNoReviewForUser(claimant)

    def claimReview(self, claimant):
        """See `ICodeReviewVote`"""
        if self.reviewer == claimant:
            return
        self.validateClaimReview(claimant)
        self.reviewer = claimant

    def validateReasignReview(self, reviewer):
        """See `ICodeReviewVote`"""
        self._validatePending()
        if not reviewer.is_team:
            self._validateNoReviewForUser(reviewer)

    def reassignReview(self, reviewer):
        """See `ICodeReviewVote`"""
        self.validateReasignReview(reviewer)
        self.reviewer = reviewer

    def delete(self):
        """See `ICodeReviewVote`"""
        if not self.is_pending:
            raise ReviewNotPending('The review is not pending.')
        self.destroySelf()