changeset_status.py
182 lines
| 6.8 KiB
| text/x-python
|
PythonLexer
Bradley M. Kuhn
|
r4187 | # -*- coding: utf-8 -*- | ||
Mads Kiilerich
|
r5376 | # This program is free software: you can redistribute it and/or modify | ||
# it under the terms of the GNU General Public License as published by | ||||
# the Free Software Foundation, either version 3 of the License, or | ||||
# (at your option) any later version. | ||||
# | ||||
# This program is distributed in the hope that it will be useful, | ||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
# GNU General Public License for more details. | ||||
# | ||||
# You should have received a copy of the GNU General Public License | ||||
# along with this program. If not, see <http://www.gnu.org/licenses/>. | ||||
Bradley M. Kuhn
|
r4187 | """ | ||
kallithea.model.changeset_status | ||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||
Thomas De Schampheleire
|
r4919 | Changeset status controller | ||
Bradley M. Kuhn
|
r4187 | |||
Bradley M. Kuhn
|
r4211 | This file was forked by the Kallithea project in July 2014. | ||
Original author and date, and relevant copyright and licensing information is below: | ||||
Bradley M. Kuhn
|
r4187 | :created_on: Apr 30, 2012 | ||
:author: marcink | ||||
Bradley M. Kuhn
|
r4211 | :copyright: (c) 2013 RhodeCode GmbH, and others. | ||
Bradley M. Kuhn
|
r4208 | :license: GPLv3, see LICENSE.md for more details. | ||
Bradley M. Kuhn
|
r4187 | """ | ||
import logging | ||||
Mads Kiilerich
|
r7718 | |||
Mads Kiilerich
|
r4737 | from sqlalchemy.orm import joinedload | ||
Bradley M. Kuhn
|
r4187 | |||
Mads Kiilerich
|
r7718 | from kallithea.model.db import ChangesetStatus, PullRequest, Repository, Session, User | ||
Bradley M. Kuhn
|
r4187 | |||
log = logging.getLogger(__name__) | ||||
Søren Løvborg
|
r6483 | class ChangesetStatusModel(object): | ||
Bradley M. Kuhn
|
r4187 | |||
def _get_status_query(self, repo, revision, pull_request, | ||||
with_revisions=False): | ||||
Søren Løvborg
|
r6424 | repo = Repository.guess_instance(repo) | ||
Bradley M. Kuhn
|
r4187 | |||
Mads Kiilerich
|
r5585 | q = ChangesetStatus.query() \ | ||
Bradley M. Kuhn
|
r4187 | .filter(ChangesetStatus.repo == repo) | ||
if not with_revisions: | ||||
Mads Kiilerich
|
r5889 | # only report the latest vote across all users! TODO: be smarter! | ||
Bradley M. Kuhn
|
r4187 | q = q.filter(ChangesetStatus.version == 0) | ||
if revision: | ||||
q = q.filter(ChangesetStatus.revision == revision) | ||||
elif pull_request: | ||||
Søren Løvborg
|
r6082 | pull_request = PullRequest.guess_instance(pull_request) | ||
Bradley M. Kuhn
|
r4187 | q = q.filter(ChangesetStatus.pull_request == pull_request) | ||
else: | ||||
raise Exception('Please specify revision or pull_request') | ||||
Mads Kiilerich
|
r4355 | q = q.order_by(ChangesetStatus.version.asc()) | ||
Bradley M. Kuhn
|
r4187 | return q | ||
Thomas De Schampheleire
|
r5039 | def _calculate_status(self, statuses): | ||
""" | ||||
Given a list of statuses, calculate the resulting status, according to | ||||
Thomas De Schampheleire
|
r5041 | the policy: approve if consensus, reject when at least one reject. | ||
Thomas De Schampheleire
|
r5039 | """ | ||
Thomas De Schampheleire
|
r5040 | if not statuses: | ||
return ChangesetStatus.STATUS_UNDER_REVIEW | ||||
Thomas De Schampheleire
|
r5039 | |||
Thomas De Schampheleire
|
r5047 | if all(st and st.status == ChangesetStatus.STATUS_APPROVED for st in statuses): | ||
Thomas De Schampheleire
|
r5040 | return ChangesetStatus.STATUS_APPROVED | ||
Thomas De Schampheleire
|
r5039 | |||
Thomas De Schampheleire
|
r5047 | if any(st and st.status == ChangesetStatus.STATUS_REJECTED for st in statuses): | ||
Thomas De Schampheleire
|
r5041 | return ChangesetStatus.STATUS_REJECTED | ||
Thomas De Schampheleire
|
r5040 | return ChangesetStatus.STATUS_UNDER_REVIEW | ||
Thomas De Schampheleire
|
r5039 | |||
Mads Kiilerich
|
r4359 | def calculate_pull_request_result(self, pull_request): | ||
Bradley M. Kuhn
|
r4187 | """ | ||
Thomas De Schampheleire
|
r5039 | Return a tuple (reviewers, pending reviewers, pull request status) | ||
Only approve and reject counts as valid votes. | ||||
Bradley M. Kuhn
|
r4187 | """ | ||
Mads Kiilerich
|
r4359 | # collect latest votes from all voters | ||
cs_statuses = dict() | ||||
for st in reversed(self.get_statuses(pull_request.org_repo, | ||||
pull_request=pull_request, | ||||
with_revisions=True)): | ||||
cs_statuses[st.author.username] = st | ||||
Thomas De Schampheleire
|
r5039 | |||
Mads Kiilerich
|
r4359 | # collect votes from official reviewers | ||
pull_request_reviewers = [] | ||||
pull_request_pending_reviewers = [] | ||||
Thomas De Schampheleire
|
r5039 | relevant_statuses = [] | ||
Mads Kiilerich
|
r5733 | for user in pull_request.get_reviewer_users(): | ||
st = cs_statuses.get(user.username) | ||||
Thomas De Schampheleire
|
r5039 | relevant_statuses.append(st) | ||
Mads Kiilerich
|
r5888 | status = ChangesetStatus.STATUS_NOT_REVIEWED if st is None else st.status | ||
if status in (ChangesetStatus.STATUS_NOT_REVIEWED, | ||||
ChangesetStatus.STATUS_UNDER_REVIEW): | ||||
Mads Kiilerich
|
r5733 | pull_request_pending_reviewers.append(user) | ||
Mads Kiilerich
|
r5888 | pull_request_reviewers.append((user, status)) | ||
Mads Kiilerich
|
r4359 | |||
Thomas De Schampheleire
|
r5039 | result = self._calculate_status(relevant_statuses) | ||
Mads Kiilerich
|
r4359 | |||
return (pull_request_reviewers, | ||||
pull_request_pending_reviewers, | ||||
result) | ||||
Bradley M. Kuhn
|
r4187 | |||
def get_statuses(self, repo, revision=None, pull_request=None, | ||||
with_revisions=False): | ||||
q = self._get_status_query(repo, revision, pull_request, | ||||
with_revisions) | ||||
Mads Kiilerich
|
r4737 | q = q.options(joinedload('author')) | ||
Bradley M. Kuhn
|
r4187 | return q.all() | ||
def get_status(self, repo, revision=None, pull_request=None, as_str=True): | ||||
""" | ||||
Returns latest status of changeset for given revision or for given | ||||
pull request. Statuses are versioned inside a table itself and | ||||
version == 0 is always the current one | ||||
:param repo: | ||||
:param revision: 40char hash or None | ||||
:param pull_request: pull_request reference | ||||
:param as_str: return status as string not object | ||||
""" | ||||
q = self._get_status_query(repo, revision, pull_request) | ||||
# need to use first here since there can be multiple statuses | ||||
# returned from pull_request | ||||
status = q.first() | ||||
if as_str: | ||||
Mads Kiilerich
|
r4318 | return str(status.status) if status else ChangesetStatus.DEFAULT | ||
Bradley M. Kuhn
|
r4187 | return status | ||
Mads Kiilerich
|
r4329 | def set_status(self, repo, status, user, comment, revision=None, | ||
Thomas De Schampheleire
|
r7350 | pull_request=None): | ||
Bradley M. Kuhn
|
r4187 | """ | ||
Creates new status for changeset or updates the old ones bumping their | ||||
Na'Tosha Bard
|
r4526 | version, leaving the current status at the value of 'status'. | ||
Bradley M. Kuhn
|
r4187 | |||
:param repo: | ||||
:param status: | ||||
:param user: | ||||
:param comment: | ||||
Mads Kiilerich
|
r4360 | :param revision: | ||
:param pull_request: | ||||
Bradley M. Kuhn
|
r4187 | """ | ||
Søren Løvborg
|
r6424 | repo = Repository.guess_instance(repo) | ||
Bradley M. Kuhn
|
r4187 | |||
q = ChangesetStatus.query() | ||||
Mads Kiilerich
|
r4360 | if revision is not None: | ||
assert pull_request is None | ||||
Bradley M. Kuhn
|
r4187 | q = q.filter(ChangesetStatus.repo == repo) | ||
q = q.filter(ChangesetStatus.revision == revision) | ||||
Mads Kiilerich
|
r4360 | revisions = [revision] | ||
else: | ||||
assert pull_request is not None | ||||
Søren Løvborg
|
r6082 | pull_request = PullRequest.guess_instance(pull_request) | ||
Mads Kiilerich
|
r4360 | repo = pull_request.org_repo | ||
q = q.filter(ChangesetStatus.repo == repo) | ||||
Bradley M. Kuhn
|
r4187 | q = q.filter(ChangesetStatus.revision.in_(pull_request.revisions)) | ||
Mads Kiilerich
|
r4360 | revisions = pull_request.revisions | ||
Bradley M. Kuhn
|
r4187 | cur_statuses = q.all() | ||
Lars Kruse
|
r6789 | # update all current statuses with older version | ||
Mads Kiilerich
|
r4360 | for st in cur_statuses: | ||
st.version += 1 | ||||
Bradley M. Kuhn
|
r4187 | |||
Mads Kiilerich
|
r4360 | new_statuses = [] | ||
for rev in revisions: | ||||
Bradley M. Kuhn
|
r4187 | new_status = ChangesetStatus() | ||
Mads Kiilerich
|
r4359 | new_status.version = 0 # default | ||
Søren Løvborg
|
r6423 | new_status.author = User.guess_instance(user) | ||
Søren Løvborg
|
r6424 | new_status.repo = Repository.guess_instance(repo) | ||
Bradley M. Kuhn
|
r4187 | new_status.status = status | ||
new_status.comment = comment | ||||
Mads Kiilerich
|
r4360 | new_status.revision = rev | ||
Bradley M. Kuhn
|
r4187 | new_status.pull_request = pull_request | ||
Mads Kiilerich
|
r4360 | new_statuses.append(new_status) | ||
Søren Løvborg
|
r6483 | Session().add(new_status) | ||
Mads Kiilerich
|
r4360 | return new_statuses | ||