Files @ fe47ff8ef3f7
Branch filter:

Location: kallithea/rhodecode/model/pull_request.py - annotation

Marcin Kuzminski
fixed tests
f29469677319
f29469677319
58c529332e7e
58c529332e7e
f29469677319
f29469677319
f29469677319
f29469677319
f29469677319
f29469677319
f29469677319
f29469677319
f29469677319
f29469677319
f29469677319
f29469677319
f29469677319
f29469677319
f29469677319
f29469677319
f29469677319
f29469677319
f29469677319
f29469677319
f29469677319
f29469677319
3bf057a7f7e8
58c529332e7e
7b092b919f4f
f29469677319
f29469677319
7b092b919f4f
f29469677319
f29469677319
7b092b919f4f
f29469677319
f29469677319
f29469677319
2b6939a77052
3bf057a7f7e8
f29469677319
f29469677319
f29469677319
f29469677319
f29469677319
17893d61792a
17893d61792a
58c529332e7e
58c529332e7e
58c529332e7e
1bc579bcd67a
1bc579bcd67a
1bc579bcd67a
1bc579bcd67a
f29469677319
f29469677319
1c2ba03c42b4
1bc579bcd67a
1c2ba03c42b4
1c2ba03c42b4
f29469677319
f29469677319
1c2ba03c42b4
f29469677319
1c2ba03c42b4
f29469677319
f29469677319
f29469677319
f29469677319
1bc579bcd67a
f29469677319
1c2ba03c42b4
f29469677319
f29469677319
f29469677319
f29469677319
f29469677319
f29469677319
f29469677319
f29469677319
1bc579bcd67a
f29469677319
f29469677319
f29469677319
f29469677319
f29469677319
c1cac08d7062
f29469677319
f29469677319
f29469677319
f29469677319
f29469677319
f29469677319
1c2ba03c42b4
f29469677319
f29469677319
f29469677319
f29469677319
3bf057a7f7e8
3f50a5e8fc4d
3f50a5e8fc4d
3f50a5e8fc4d
3f50a5e8fc4d
3f50a5e8fc4d
3f50a5e8fc4d
3f50a5e8fc4d
3f50a5e8fc4d
3f50a5e8fc4d
3f50a5e8fc4d
3f50a5e8fc4d
3f50a5e8fc4d
3f50a5e8fc4d
3f50a5e8fc4d
3f50a5e8fc4d
3f50a5e8fc4d
3f50a5e8fc4d
3f50a5e8fc4d
3f50a5e8fc4d
3f50a5e8fc4d
3f50a5e8fc4d
3f50a5e8fc4d
3f50a5e8fc4d
3f50a5e8fc4d
3f50a5e8fc4d
3f50a5e8fc4d
3f50a5e8fc4d
3f50a5e8fc4d
58c529332e7e
58c529332e7e
58c529332e7e
58c529332e7e
58c529332e7e
58c529332e7e
3bf057a7f7e8
3bf057a7f7e8
3bf057a7f7e8
3bf057a7f7e8
3bf057a7f7e8
3bf057a7f7e8
3bf057a7f7e8
3bf057a7f7e8
3bf057a7f7e8
3bf057a7f7e8
3bf057a7f7e8
3bf057a7f7e8
3bf057a7f7e8
3bf057a7f7e8
3bf057a7f7e8
3bf057a7f7e8
3bf057a7f7e8
3bf057a7f7e8
3bf057a7f7e8
2b6939a77052
2b6939a77052
2b6939a77052
3bf057a7f7e8
3bf057a7f7e8
3bf057a7f7e8
3bf057a7f7e8
f79320e47e99
f79320e47e99
f79320e47e99
f79320e47e99
f79320e47e99
f79320e47e99
f79320e47e99
f79320e47e99
f79320e47e99
f79320e47e99
f79320e47e99
f79320e47e99
f79320e47e99
f79320e47e99
3bf057a7f7e8
3bf057a7f7e8
3bf057a7f7e8
3bf057a7f7e8
3bf057a7f7e8
3bf057a7f7e8
3bf057a7f7e8
3bf057a7f7e8
3bf057a7f7e8
3bf057a7f7e8
3bf057a7f7e8
3bf057a7f7e8
3bf057a7f7e8
3bf057a7f7e8
3bf057a7f7e8
3bf057a7f7e8
3bf057a7f7e8
3bf057a7f7e8
3bf057a7f7e8
3bf057a7f7e8
3bf057a7f7e8
2b6939a77052
2b6939a77052
2b6939a77052
2b6939a77052
2b6939a77052
2b6939a77052
3bf057a7f7e8
3bf057a7f7e8
3bf057a7f7e8
2b6939a77052
2b6939a77052
3bf057a7f7e8
2b6939a77052
2b6939a77052
2b6939a77052
2b6939a77052
3bf057a7f7e8
3bf057a7f7e8
3bf057a7f7e8
3bf057a7f7e8
3bf057a7f7e8
3bf057a7f7e8
3bf057a7f7e8
3bf057a7f7e8
3bf057a7f7e8
3bf057a7f7e8
3bf057a7f7e8
3bf057a7f7e8
3bf057a7f7e8
3bf057a7f7e8
3bf057a7f7e8
3bf057a7f7e8
3bf057a7f7e8
3bf057a7f7e8
3bf057a7f7e8
3bf057a7f7e8
3bf057a7f7e8
3bf057a7f7e8
3bf057a7f7e8
3bf057a7f7e8
3bf057a7f7e8
3bf057a7f7e8
3bf057a7f7e8
3bf057a7f7e8
3bf057a7f7e8
2b6939a77052
2b6939a77052
2b6939a77052
2b6939a77052
2b6939a77052
3bf057a7f7e8
# -*- coding: utf-8 -*-
"""
    rhodecode.model.pull_request
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    pull request model for RhodeCode

    :created_on: Jun 6, 2012
    :author: marcink
    :copyright: (C) 2012-2012 Marcin Kuzminski <marcin@python-works.com>
    :license: GPLv3, see COPYING for more details.
"""
# 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/>.

import logging
import binascii
import datetime

from pylons.i18n.translation import _

from rhodecode.model.meta import Session
from rhodecode.lib import helpers as h
from rhodecode.model import BaseModel
from rhodecode.model.db import PullRequest, PullRequestReviewers, Notification
from rhodecode.model.notification import NotificationModel
from rhodecode.lib.utils2 import safe_unicode

from rhodecode.lib.vcs.utils.hgcompat import discovery, localrepo, scmutil

log = logging.getLogger(__name__)


class PullRequestModel(BaseModel):

    cls = PullRequest

    def __get_pull_request(self, pull_request):
        return self._get_instance(PullRequest, pull_request)

    def get_all(self, repo):
        repo = self._get_repo(repo)
        return PullRequest.query().filter(PullRequest.other_repo == repo).all()

    def create(self, created_by, org_repo, org_ref, other_repo,
               other_ref, revisions, reviewers, title, description=None):

        created_by_user = self._get_user(created_by)
        org_repo = self._get_repo(org_repo)
        other_repo = self._get_repo(other_repo)

        new = PullRequest()
        new.org_repo = org_repo
        new.org_ref = org_ref
        new.other_repo = other_repo
        new.other_ref = other_ref
        new.revisions = revisions
        new.title = title
        new.description = description
        new.author = created_by_user
        self.sa.add(new)
        Session().flush()
        #members
        for member in reviewers:
            _usr = self._get_user(member)
            reviewer = PullRequestReviewers(_usr, new)
            self.sa.add(reviewer)

        #notification to reviewers
        notif = NotificationModel()

        subject = safe_unicode(
            h.link_to(
              _('%(user)s wants you to review pull request #%(pr_id)s') % \
                {'user': created_by_user.username,
                 'pr_id': new.pull_request_id},
              h.url('pullrequest_show', repo_name=other_repo.repo_name,
                    pull_request_id=new.pull_request_id,
                    qualified=True,
              )
            )
        )
        body = description
        notif.create(created_by=created_by_user, subject=subject, body=body,
                     recipients=reviewers,
                     type_=Notification.TYPE_PULL_REQUEST,)

        return new

    def update_reviewers(self, pull_request, reviewers_ids):
        reviewers_ids = set(reviewers_ids)
        pull_request = self.__get_pull_request(pull_request)
        current_reviewers = PullRequestReviewers.query()\
                            .filter(PullRequestReviewers.pull_request==
                                   pull_request)\
                            .all()
        current_reviewers_ids = set([x.user.user_id for x in current_reviewers])

        to_add = reviewers_ids.difference(current_reviewers_ids)
        to_remove = current_reviewers_ids.difference(reviewers_ids)

        log.debug("Adding %s reviewers" % to_add)
        log.debug("Removing %s reviewers" % to_remove)

        for uid in to_add:
            _usr = self._get_user(uid)
            reviewer = PullRequestReviewers(_usr, pull_request)
            self.sa.add(reviewer)

        for uid in to_remove:
            reviewer = PullRequestReviewers.query()\
                    .filter(PullRequestReviewers.user_id==uid,
                            PullRequestReviewers.pull_request==pull_request)\
                    .scalar()
            if reviewer:
                self.sa.delete(reviewer)

    def close_pull_request(self, pull_request):
        pull_request = self.__get_pull_request(pull_request)
        pull_request.status = PullRequest.STATUS_CLOSED
        pull_request.updated_on = datetime.datetime.now()
        self.sa.add(pull_request)

    def _get_changesets(self, org_repo, org_ref, other_repo, other_ref,
                        discovery_data):
        """
        Returns a list of changesets that are incoming from org_repo@org_ref
        to other_repo@other_ref

        :param org_repo:
        :type org_repo:
        :param org_ref:
        :type org_ref:
        :param other_repo:
        :type other_repo:
        :param other_ref:
        :type other_ref:
        :param tmp:
        :type tmp:
        """
        changesets = []
        #case two independent repos
        common, incoming, rheads = discovery_data
        if org_repo != other_repo and incoming:
            revs = org_repo._repo.changelog.findmissing(common, rheads)

            for cs in reversed(map(binascii.hexlify, revs)):
                changesets.append(org_repo.get_changeset(cs))
        else:
            _revset_predicates = {
                    'branch': 'branch',
                    'book': 'bookmark',
                    'tag': 'tag',
                    'rev': 'id',
                }

            revs = [
                "ancestors(%s('%s')) and not ancestors(%s('%s'))" % (
                    _revset_predicates[org_ref[0]], org_ref[1],
                    _revset_predicates[other_ref[0]], other_ref[1]
               )
            ]

            out = scmutil.revrange(org_repo._repo, revs)
            for cs in reversed(out):
                changesets.append(org_repo.get_changeset(cs))

        return changesets

    def _get_discovery(self, org_repo, org_ref, other_repo, other_ref):
        """
        Get's mercurial discovery data used to calculate difference between
        repos and refs

        :param org_repo:
        :type org_repo:
        :param org_ref:
        :type org_ref:
        :param other_repo:
        :type other_repo:
        :param other_ref:
        :type other_ref:
        """

        _org_repo = org_repo._repo
        org_rev_type, org_rev = org_ref

        _other_repo = other_repo._repo
        other_rev_type, other_rev = other_ref

        log.debug('Doing discovery for %s@%s vs %s@%s' % (
                        org_repo, org_ref, other_repo, other_ref)
        )
        #log.debug('Filter heads are %s[%s]' % ('', org_ref[1]))
        org_peer = localrepo.locallegacypeer(_org_repo.local())
        tmp = discovery.findcommonincoming(
                  repo=_other_repo,  # other_repo we check for incoming
                  remote=org_peer,  # org_repo source for incoming
                  heads=[_other_repo[other_rev].node(),
                         _org_repo[org_rev].node()],
                  force=False
        )
        return tmp

    def get_compare_data(self, org_repo, org_ref, other_repo, other_ref):
        """
        Returns a tuple of incomming changesets, and discoverydata cache

        :param org_repo:
        :type org_repo:
        :param org_ref:
        :type org_ref:
        :param other_repo:
        :type other_repo:
        :param other_ref:
        :type other_ref:
        """

        if len(org_ref) != 2 or not isinstance(org_ref, (list, tuple)):
            raise Exception('org_ref must be a two element list/tuple')

        if len(other_ref) != 2 or not isinstance(org_ref, (list, tuple)):
            raise Exception('other_ref must be a two element list/tuple')

        discovery_data = self._get_discovery(org_repo.scm_instance,
                                           org_ref,
                                           other_repo.scm_instance,
                                           other_ref)
        cs_ranges = self._get_changesets(org_repo.scm_instance,
                                         org_ref,
                                         other_repo.scm_instance,
                                         other_ref,
                                         discovery_data)

        return cs_ranges, discovery_data