changeset 2443:fd0a822481ec codereview

- added commenting to pull requests - it's now possible to change pull request status via changeset status option - status flags with associated pull requests are links to pull request they been changed in
author Marcin Kuzminski <marcin@python-works.com>
date Mon, 11 Jun 2012 00:58:24 +0200
parents 3bf057a7f7e8
children b45e9fd75ac0
files rhodecode/config/routing.py rhodecode/controllers/changeset.py rhodecode/controllers/pullrequests.py rhodecode/model/changeset_status.py rhodecode/model/comment.py rhodecode/model/db.py rhodecode/model/notification.py rhodecode/templates/changelog/changelog.html rhodecode/templates/pullrequests/pullrequest_show.html
diffstat 9 files changed, 197 insertions(+), 71 deletions(-) [+]
line wrap: on
line diff
--- a/rhodecode/config/routing.py	Sun Jun 10 23:18:27 2012 +0200
+++ b/rhodecode/config/routing.py	Mon Jun 11 00:58:24 2012 +0200
@@ -457,6 +457,12 @@
                  action='show_all', conditions=dict(function=check_repo,
                                                 method=["GET"]))
 
+    rmap.connect('pullrequest_comment',
+                 '/{repo_name:.*}/pull-request-comment/{pull_request_id}',
+                 controller='pullrequests',
+                 action='comment', conditions=dict(function=check_repo,
+                                                method=["POST"]))
+
     rmap.connect('summary_home', '/{repo_name:.*}/summary',
                 controller='summary', conditions=dict(function=check_repo))
 
--- a/rhodecode/controllers/changeset.py	Sun Jun 10 23:18:27 2012 +0200
+++ b/rhodecode/controllers/changeset.py	Mon Jun 11 00:58:24 2012 +0200
@@ -390,10 +390,10 @@
         if status and change_status:
             ChangesetStatusModel().set_status(
                 c.rhodecode_db_repo.repo_id,
-                revision,
                 status,
                 c.rhodecode_user.user_id,
                 comm,
+                revision=revision,
             )
         action_logger(self.rhodecode_user,
                       'user_commented_revision:%s' % revision,
--- a/rhodecode/controllers/pullrequests.py	Sun Jun 10 23:18:27 2012 +0200
+++ b/rhodecode/controllers/pullrequests.py	Mon Jun 11 00:58:24 2012 +0200
@@ -24,19 +24,20 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 import logging
 import traceback
-import binascii
 
 from webob.exc import HTTPNotFound
 
 from pylons import request, response, session, tmpl_context as c, url
 from pylons.controllers.util import abort, redirect
 from pylons.i18n.translation import _
+from pylons.decorators import jsonify
 
 from rhodecode.lib.base import BaseRepoController, render
 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
 from rhodecode.lib import helpers as h
 from rhodecode.lib import diffs
-from rhodecode.model.db import User, PullRequest, Repository, ChangesetStatus
+from rhodecode.lib.utils import action_logger
+from rhodecode.model.db import User, PullRequest, ChangesetStatus
 from rhodecode.model.pull_request import PullRequestModel
 from rhodecode.model.meta import Session
 from rhodecode.model.repo import RepoModel
@@ -208,9 +209,56 @@
                           .get_comments(c.rhodecode_db_repo.repo_id,
                                         pull_request=pull_request_id)
 
-        # changeset(pull-request) statuse
+        # changeset(pull-request) status
         c.current_changeset_status = ChangesetStatusModel()\
-                              .get_status(c.rhodecode_db_repo.repo_id,
-                                          pull_request=pull_request_id)
+                              .get_status(c.pull_request.org_repo,
+                                          pull_request=c.pull_request)
         c.changeset_statuses = ChangesetStatus.STATUSES
         return render('/pullrequests/pullrequest_show.html')
+
+    @jsonify
+    def comment(self, repo_name, pull_request_id):
+
+        status = request.POST.get('changeset_status')
+        change_status = request.POST.get('change_changeset_status')
+
+        comm = ChangesetCommentsModel().create(
+            text=request.POST.get('text'),
+            repo_id=c.rhodecode_db_repo.repo_id,
+            user_id=c.rhodecode_user.user_id,
+            pull_request=pull_request_id,
+            f_path=request.POST.get('f_path'),
+            line_no=request.POST.get('line'),
+            status_change=(ChangesetStatus.get_status_lbl(status) 
+                           if status and change_status else None)
+        )
+
+        # get status if set !
+        if status and change_status:
+            ChangesetStatusModel().set_status(
+                c.rhodecode_db_repo.repo_id,
+                status,
+                c.rhodecode_user.user_id,
+                comm,
+                pull_request=pull_request_id
+            )
+        action_logger(self.rhodecode_user,
+                      'user_commented_pull_request:%s' % pull_request_id,
+                      c.rhodecode_db_repo, self.ip_addr, self.sa)
+
+        Session.commit()
+
+        if not request.environ.get('HTTP_X_PARTIAL_XHR'):
+            return redirect(h.url('pullrequest_show', repo_name=repo_name,
+                                  pull_request_id=pull_request_id))
+
+        data = {
+           'target_id': h.safeid(h.safe_unicode(request.POST.get('f_path'))),
+        }
+        if comm:
+            c.co = comm
+            data.update(comm.get_dict())
+            data.update({'rendered_text':
+                         render('changeset/changeset_comment_block.html')})
+
+        return data
\ No newline at end of file
--- a/rhodecode/model/changeset_status.py	Sun Jun 10 23:18:27 2012 +0200
+++ b/rhodecode/model/changeset_status.py	Mon Jun 11 00:58:24 2012 +0200
@@ -66,12 +66,15 @@
         else:
             raise Exception('Please specify revision or pull_request')
 
-        status = q.scalar()
+        # need to use first here since there can be multiple statuses
+        # returned from pull_request
+        status = q.first()
         status = status.status if status else status
         st = status or ChangesetStatus.DEFAULT
         return str(st)
 
-    def set_status(self, repo, revision, status, user, comment):
+    def set_status(self, repo, status, user, comment, revision=None,
+                   pull_request=None):
         """
         Creates new status for changeset or updates the old ones bumping their
         version, leaving the current status at
@@ -89,20 +92,48 @@
         """
         repo = self._get_repo(repo)
 
-        cur_statuses = ChangesetStatus.query()\
-            .filter(ChangesetStatus.repo == repo)\
-            .filter(ChangesetStatus.revision == revision)\
-            .all()
+        q = ChangesetStatus.query()
+
+        if revision:
+            q = q.filter(ChangesetStatus.repo == repo)
+            q = q.filter(ChangesetStatus.revision == revision)
+        elif pull_request:
+            pull_request = self.__get_pull_request(pull_request)
+            q = q.filter(ChangesetStatus.repo == pull_request.org_repo)
+            q = q.filter(ChangesetStatus.pull_request == pull_request)
+        cur_statuses = q.all()
+
         if cur_statuses:
             for st in cur_statuses:
                 st.version += 1
                 self.sa.add(st)
-        new_status = ChangesetStatus()
-        new_status.author = self._get_user(user)
-        new_status.repo = self._get_repo(repo)
-        new_status.status = status
-        new_status.revision = revision
-        new_status.comment = comment
-        self.sa.add(new_status)
-        return new_status
+
+        def _create_status(user, repo, status, comment, revision, pull_request):
+            new_status = ChangesetStatus()
+            new_status.author = self._get_user(user)
+            new_status.repo = self._get_repo(repo)
+            new_status.status = status
+            new_status.comment = comment
+            new_status.revision = revision
+            new_status.pull_request = pull_request
+            return new_status
 
+        if revision:
+            new_status = _create_status(user=user, repo=repo, status=status,
+                           comment=comment, revision=revision, 
+                           pull_request=None)
+            self.sa.add(new_status)
+            return new_status
+        elif pull_request:
+            #pull request can have more than one revision associated to it
+            #we need to create new version for each one
+            new_statuses = []
+            repo = pull_request.org_repo
+            for rev in pull_request.revisions:
+                new_status = _create_status(user=user, repo=repo,
+                                            status=status, comment=comment,
+                                            revision=rev,
+                                            pull_request=pull_request)
+                new_statuses.append(new_status)
+                self.sa.add(new_status)
+            return new_statuses
--- a/rhodecode/model/comment.py	Sun Jun 10 23:18:27 2012 +0200
+++ b/rhodecode/model/comment.py	Mon Jun 11 00:58:24 2012 +0200
@@ -55,38 +55,54 @@
                 user_objects.append(user_obj)
         return user_objects
 
-    def create(self, text, repo_id, user_id, revision, f_path=None,
-               line_no=None, status_change=None):
+    def create(self, text, repo_id, user_id, revision=None, pull_request=None,
+               f_path=None, line_no=None, status_change=None):
         """
-        Creates new comment for changeset. IF status_change is not none
-        this comment is associated with a status change of changeset
+        Creates new comment for changeset or pull request.
+        IF status_change is not none this comment is associated with a 
+        status change of changeset or changesets associated with pull request
 
         :param text:
         :param repo_id:
         :param user_id:
         :param revision:
+        :param pull_request:
         :param f_path:
         :param line_no:
         :param status_change:
         """
+        if not text:
+            return
 
-        if text:
-            repo = Repository.get(repo_id)
+        repo = Repository.get(repo_id)
+        comment = ChangesetComment()
+        comment.repo = repo
+        comment.user_id = user_id
+        comment.text = text
+        comment.f_path = f_path
+        comment.line_no = line_no
+
+        if revision:
             cs = repo.scm_instance.get_changeset(revision)
             desc = "%s - %s" % (cs.short_id, h.shorter(cs.message, 256))
             author_email = cs.author_email
-            comment = ChangesetComment()
-            comment.repo = repo
-            comment.user_id = user_id
             comment.revision = revision
-            comment.text = text
-            comment.f_path = f_path
-            comment.line_no = line_no
+        elif pull_request:
+            pull_request = self.__get_pull_request(pull_request)
+            comment.pull_request = pull_request
+            desc = ''
+        else:
+            raise Exception('Please specify revision or pull_request_id')
 
-            self.sa.add(comment)
-            self.sa.flush()
-            # make notification
-            line = ''
+        self.sa.add(comment)
+        self.sa.flush()
+
+        # make notification
+        line = ''
+        body = text
+
+        #changeset
+        if revision:
             if line_no:
                 line = _('on line %s') % line_no
             subj = safe_unicode(
@@ -99,34 +115,41 @@
                           )
                 )
             )
-
-            body = text
-
+            notification_type = Notification.TYPE_CHANGESET_COMMENT
             # get the current participants of this changeset
             recipients = ChangesetComment.get_users(revision=revision)
-
             # add changeset author if it's in rhodecode system
             recipients += [User.get_by_email(author_email)]
+        #pull request
+        elif pull_request:
+            #TODO: make this something usefull
+            subj = 'commented on pull request something...'
+            notification_type = Notification.TYPE_PULL_REQUEST_COMMENT
+            # get the current participants of this pull request
+            recipients = ChangesetComment.get_users(pull_request_id=
+                                                pull_request.pull_request_id)
+            # add pull request author
+            recipients += [pull_request.author]
 
-            # create notification objects, and emails
+        # create notification objects, and emails
+        NotificationModel().create(
+          created_by=user_id, subject=subj, body=body,
+          recipients=recipients, type_=notification_type,
+          email_kwargs={'status_change': status_change}
+        )
+
+        mention_recipients = set(self._extract_mentions(body))\
+                                .difference(recipients)
+        if mention_recipients:
+            subj = _('[Mention]') + ' ' + subj
             NotificationModel().create(
-              created_by=user_id, subject=subj, body=body,
-              recipients=recipients, type_=Notification.TYPE_CHANGESET_COMMENT,
-              email_kwargs={'status_change': status_change}
+                created_by=user_id, subject=subj, body=body,
+                recipients=mention_recipients,
+                type_=notification_type,
+                email_kwargs={'status_change': status_change}
             )
 
-            mention_recipients = set(self._extract_mentions(body))\
-                                    .difference(recipients)
-            if mention_recipients:
-                subj = _('[Mention]') + ' ' + subj
-                NotificationModel().create(
-                    created_by=user_id, subject=subj, body=body,
-                    recipients=mention_recipients,
-                    type_=Notification.TYPE_CHANGESET_COMMENT,
-                    email_kwargs={'status_change': status_change}
-                )
-
-            return comment
+        return comment
 
     def delete(self, comment):
         """
--- a/rhodecode/model/db.py	Sun Jun 10 23:18:27 2012 +0200
+++ b/rhodecode/model/db.py	Mon Jun 11 00:58:24 2012 +0200
@@ -766,7 +766,12 @@
             statuses = statuses.filter(ChangesetStatus.revision.in_(revisions))
         grouped = {}
         for stat in statuses.all():
-            grouped[stat.revision] = [str(stat.status), stat.status_lbl]
+            pr_id = pr_repo = None
+            if stat.pull_request:
+                pr_id = stat.pull_request.pull_request_id
+                pr_repo = stat.pull_request.other_repo.repo_name
+            grouped[stat.revision] = [str(stat.status), stat.status_lbl,
+                                      pr_id, pr_repo]
         return grouped
 
     #==========================================================================
@@ -1336,17 +1341,21 @@
     pull_request = relationship('PullRequest', lazy='joined')
 
     @classmethod
-    def get_users(cls, revision):
+    def get_users(cls, revision=None, pull_request_id=None):
         """
-        Returns user associated with this changesetComment. ie those
+        Returns user associated with this ChangesetComment. ie those
         who actually commented
 
         :param cls:
         :param revision:
         """
-        return Session.query(User)\
-                .filter(cls.revision == revision)\
-                .join(ChangesetComment.author).all()
+        q = Session.query(User)\
+                .join(ChangesetComment.author)
+        if revision:
+            q = q.filter(cls.revision == revision)
+        elif pull_request_id:
+            q = q.filter(cls.pull_request_id == pull_request_id)
+        return q.all()
 
 
 class ChangesetStatus(Base, BaseModel):
@@ -1457,6 +1466,7 @@
     TYPE_MENTION = u'mention'
     TYPE_REGISTRATION = u'registration'
     TYPE_PULL_REQUEST = u'pull_request'
+    TYPE_PULL_REQUEST_COMMENT = u'pull_request_comment'
 
     notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
     subject = Column('subject', Unicode(512), nullable=True)
--- a/rhodecode/model/notification.py	Sun Jun 10 23:18:27 2012 +0200
+++ b/rhodecode/model/notification.py	Mon Jun 11 00:58:24 2012 +0200
@@ -198,13 +198,15 @@
         Creates a human readable description based on properties
         of notification object
         """
-
+        #alias
+        _n = notification
         _map = {
-            notification.TYPE_CHANGESET_COMMENT: _('commented on commit'),
-            notification.TYPE_MESSAGE: _('sent message'),
-            notification.TYPE_MENTION: _('mentioned you'),
-            notification.TYPE_REGISTRATION: _('registered in RhodeCode'),
-            notification.TYPE_PULL_REQUEST: _('opened new pull request')
+            _n.TYPE_CHANGESET_COMMENT: _('commented on commit'),
+            _n.TYPE_MESSAGE: _('sent message'),
+            _n.TYPE_MENTION: _('mentioned you'),
+            _n.TYPE_REGISTRATION: _('registered in RhodeCode'),
+            _n.TYPE_PULL_REQUEST: _('opened new pull request'),
+            _n.TYPE_PULL_REQUEST_COMMENT: _('commented on pull request')
         }
 
         tmpl = "%(user)s %(action)s %(when)s"
--- a/rhodecode/templates/changelog/changelog.html	Sun Jun 10 23:18:27 2012 +0200
+++ b/rhodecode/templates/changelog/changelog.html	Mon Jun 11 00:58:24 2012 +0200
@@ -85,7 +85,13 @@
                                         <div class="changeset-status-container">
                                             %if c.statuses.get(cs.raw_id):
                                               <div title="${_('Changeset status')}" class="changeset-status-lbl">${c.statuses.get(cs.raw_id)[1]}</div>
-                                              <div class="changeset-status-ico"><img src="${h.url('/images/icons/flag_status_%s.png' % c.statuses.get(cs.raw_id)[0])}" /></div>
+                                              <div class="changeset-status-ico">
+                                              %if c.statuses.get(cs.raw_id)[2]:
+                                                <a class="tooltip" title="${_('Click to open associated pull request')}" href="${h.url('pullrequest_show',repo_name=c.statuses.get(cs.raw_id)[3],pull_request_id=c.statuses.get(cs.raw_id)[2])}"><img src="${h.url('/images/icons/flag_status_%s.png' % c.statuses.get(cs.raw_id)[0])}" /></a>
+                                              %else:
+                                                <img src="${h.url('/images/icons/flag_status_%s.png' % c.statuses.get(cs.raw_id)[0])}" />
+                                              %endif
+                                              </div>
                                             %endif
                                         </div>
 									</div>
--- a/rhodecode/templates/pullrequests/pullrequest_show.html	Sun Jun 10 23:18:27 2012 +0200
+++ b/rhodecode/templates/pullrequests/pullrequest_show.html	Mon Jun 11 00:58:24 2012 +0200
@@ -70,8 +70,8 @@
     ##${comment.comment_inline_form(c.changeset)}
 
     ## render comments main comments form and it status
-    ##${comment.comments(h.url('pull_request_comment', repo_name=c.repo_name, pull_request_id=c.pull_request.pull_request_id),
-    ##                   c.current_changeset_status)}
+    ${comment.comments(h.url('pullrequest_comment', repo_name=c.repo_name, pull_request_id=c.pull_request.pull_request_id),
+                       c.current_changeset_status)}
     
 </div>